From 1513e739c65701e7f5735e8126cfa24a0b59834c Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Wed, 20 Oct 2021 16:40:13 +0300 Subject: [PATCH] [FE 1.0] Prohibit confusing syntax inside when branches ^KT-48385 Fixed --- ...CompilerTestFE10TestdataTestGenerated.java | 18 ++ ...irOldFrontendDiagnosticsTestGenerated.java | 18 ++ ...DiagnosticsWithLightTreeTestGenerated.java | 18 ++ .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../ConfusingWhenBranchSyntaxChecker.kt | 51 ++++ .../PatternMatchingTypingVisitor.kt | 3 + .../tests/when/ExhaustiveBooleanComplex.kt | 4 +- ...precatedSyntaxInConditionsNoSubject.fir.kt | 61 +++++ .../deprecatedSyntaxInConditionsNoSubject.kt | 61 +++++ .../deprecatedSyntaxInConditionsNoSubject.txt | 19 ++ .../deprecatedSyntaxInConditions_after.fir.kt | 236 ++++++++++++++++++ .../deprecatedSyntaxInConditions_after.kt | 236 ++++++++++++++++++ .../deprecatedSyntaxInConditions_after.txt | 40 +++ ...deprecatedSyntaxInConditions_before.fir.kt | 236 ++++++++++++++++++ .../deprecatedSyntaxInConditions_before.kt | 236 ++++++++++++++++++ .../deprecatedSyntaxInConditions_before.txt | 41 +++ ...tiveBooleanWhenWithUntrivialConst_error.kt | 6 +- ...veBooleanWhenWithUntrivialConst_warning.kt | 6 +- .../test/runners/DiagnosticTestGenerated.java | 18 ++ .../p-2/neg/11.1.kt | 4 +- .../p-2/neg/3.1.kt | 4 +- .../p-2/pos/11.1.kt | 4 +- .../p-2/pos/3.1.kt | 4 +- .../when-expression/p-5/pos/1.1.kt | 12 +- .../when-expression/p-6/pos/5.1.kt | 8 +- .../when-expression/p-6/pos/5.2.kt | 4 +- .../kotlin/config/LanguageVersionSettings.kt | 1 + 28 files changed, 1323 insertions(+), 28 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ConfusingWhenBranchSyntaxChecker.kt create mode 100644 compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.fir.kt create mode 100644 compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.kt create mode 100644 compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.txt create mode 100644 compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.fir.kt create mode 100644 compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.kt create mode 100644 compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.txt create mode 100644 compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.fir.kt create mode 100644 compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.kt create mode 100644 compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.txt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 7c944f26b04..d4e2323c726 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -32095,6 +32095,24 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/when/CommaInWhenConditionWithoutArgument.kt"); } + @Test + @TestMetadata("deprecatedSyntaxInConditionsNoSubject.kt") + public void testDeprecatedSyntaxInConditionsNoSubject() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.kt"); + } + + @Test + @TestMetadata("deprecatedSyntaxInConditions_after.kt") + public void testDeprecatedSyntaxInConditions_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.kt"); + } + + @Test + @TestMetadata("deprecatedSyntaxInConditions_before.kt") + public void testDeprecatedSyntaxInConditions_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.kt"); + } + @Test @TestMetadata("DuplicatedLabels.kt") public void testDuplicatedLabels() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 3bae45c59f7..64829c82a08 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -32095,6 +32095,24 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/when/CommaInWhenConditionWithoutArgument.kt"); } + @Test + @TestMetadata("deprecatedSyntaxInConditionsNoSubject.kt") + public void testDeprecatedSyntaxInConditionsNoSubject() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.kt"); + } + + @Test + @TestMetadata("deprecatedSyntaxInConditions_after.kt") + public void testDeprecatedSyntaxInConditions_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.kt"); + } + + @Test + @TestMetadata("deprecatedSyntaxInConditions_before.kt") + public void testDeprecatedSyntaxInConditions_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.kt"); + } + @Test @TestMetadata("DuplicatedLabels.kt") public void testDuplicatedLabels() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 5cbbb2e1b57..0978fac834b 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -32095,6 +32095,24 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/when/CommaInWhenConditionWithoutArgument.kt"); } + @Test + @TestMetadata("deprecatedSyntaxInConditionsNoSubject.kt") + public void testDeprecatedSyntaxInConditionsNoSubject() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.kt"); + } + + @Test + @TestMetadata("deprecatedSyntaxInConditions_after.kt") + public void testDeprecatedSyntaxInConditions_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.kt"); + } + + @Test + @TestMetadata("deprecatedSyntaxInConditions_before.kt") + public void testDeprecatedSyntaxInConditions_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.kt"); + } + @Test @TestMetadata("DuplicatedLabels.kt") public void testDuplicatedLabels() throws Exception { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index fdc2916d89b..0a93c1144fd 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -997,6 +997,7 @@ public interface Errors { DiagnosticFactory0 SENSELESS_NULL_IN_WHEN = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 INVALID_IF_AS_EXPRESSION = DiagnosticFactory0.create(ERROR); + DiagnosticFactoryForDeprecation0 CONFUSING_BRANCH_CONDITION = DiagnosticFactoryForDeprecation0.create(LanguageFeature.ProhibitConfusingSyntaxInWhenBranches); // Nullability diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index e262dfc7924..305ec4009bc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -833,6 +833,7 @@ public class DefaultErrorMessages { MAP.put(SENSELESS_NULL_IN_WHEN, "Expression under 'when' is never equal to null"); MAP.put(INVALID_IF_AS_EXPRESSION, "'if' must have both main and 'else' branches if used as an expression"); + MAP.put(CONFUSING_BRANCH_CONDITION, "The logical expressions may be understood ambiguously in when with subject branches. Please wrap it with parenthesis"); MAP.put(OVERRIDING_FINAL_MEMBER, "''{0}'' in ''{1}'' is final and cannot be overridden", NAME, NAME); MAP.put(CANNOT_WEAKEN_ACCESS_PRIVILEGE, "Cannot weaken access privilege ''{0}'' for ''{1}'' in ''{2}''", VISIBILITY, NAME, NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ConfusingWhenBranchSyntaxChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ConfusingWhenBranchSyntaxChecker.kt new file mode 100644 index 00000000000..2014df50a1a --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ConfusingWhenBranchSyntaxChecker.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.checkers + +import com.intellij.psi.tree.TokenSet +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.diagnostics.Errors.CONFUSING_BRANCH_CONDITION +import org.jetbrains.kotlin.lexer.KtTokens.* +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingTrace + +object ConfusingWhenBranchSyntaxChecker { + private val prohibitedTokens = TokenSet.create( + IN_KEYWORD, NOT_IN, + LT, LTEQ, GT, GTEQ, + EQEQ, EXCLEQ, EQEQEQ, EXCLEQEQEQ, + ANDAND, OROR + ) + + fun check(whenExpression: KtWhenExpression, languageVersionSettings: LanguageVersionSettings, trace: BindingTrace) { + if (whenExpression.subjectExpression == null && whenExpression.subjectVariable == null) return + for (entry in whenExpression.entries) { + for (condition in entry.conditions) { + checkCondition(condition, languageVersionSettings, trace) + } + } + } + + private fun checkCondition(condition: KtWhenCondition, languageVersionSettings: LanguageVersionSettings, trace: BindingTrace) { + when (condition) { + is KtWhenConditionWithExpression -> checkConditionExpression(condition.expression, languageVersionSettings, trace) + is KtWhenConditionInRange -> checkConditionExpression(condition.rangeExpression, languageVersionSettings, trace) + } + } + + private fun checkConditionExpression(rawExpression: KtExpression?, languageVersionSettings: LanguageVersionSettings, trace: BindingTrace) { + if (rawExpression == null) return + if (rawExpression is KtParenthesizedExpression) return + val shouldReport = when (val expression = KtPsiUtil.safeDeparenthesize(rawExpression)) { + is KtIsExpression -> true + is KtBinaryExpression -> expression.operationToken in prohibitedTokens + else -> false + } + if (shouldReport) { + trace.report(CONFUSING_BRANCH_CONDITION.on(languageVersionSettings, rawExpression)) + } + } +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt index d4b6cbe149b..93679c94f49 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/PatternMatchingTypingVisitor.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.calls.checkers.RttiOperation import org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT import org.jetbrains.kotlin.resolve.calls.smartcasts.* import org.jetbrains.kotlin.resolve.calls.util.CallMaker +import org.jetbrains.kotlin.resolve.checkers.ConfusingWhenBranchSyntaxChecker import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonCallChecker import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind @@ -249,6 +250,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping val branchesType = branchesTypeInfo.type ?: return noTypeInfo(resultDataFlowInfo) val resultType = components.dataFlowAnalyzer.checkType(branchesType, expression, contextWithExpectedType) + ConfusingWhenBranchSyntaxChecker.check(expression, contextWithExpectedType.languageVersionSettings, trace) + return createTypeInfo(resultType, resultDataFlowInfo, branchesTypeInfo.jumpOutPossible, contextWithExpectedType.dataFlowInfo) } diff --git a/compiler/testData/diagnostics/tests/when/ExhaustiveBooleanComplex.kt b/compiler/testData/diagnostics/tests/when/ExhaustiveBooleanComplex.kt index 629427837f8..49c0b9009f7 100644 --- a/compiler/testData/diagnostics/tests/when/ExhaustiveBooleanComplex.kt +++ b/compiler/testData/diagnostics/tests/when/ExhaustiveBooleanComplex.kt @@ -14,7 +14,7 @@ fun foo(arg: Boolean): String { // Must be exhaustive return when(arg) { - 2 == 2 -> "truth" - 2 == 1 -> "falsehood" + (2 == 2) -> "truth" + (2 == 1) -> "falsehood" } } diff --git a/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.fir.kt b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.fir.kt new file mode 100644 index 00000000000..7876bffa25b --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.fir.kt @@ -0,0 +1,61 @@ +// LANGUAGE: +ProhibitConfusingSyntaxInWhenBranches +// DIAGNOSTICS: -INCOMPATIBLE_TYPES, -NON_EXHAUSTIVE_WHEN_STATEMENT, -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE +// ISSUE: KT-48385 + +operator fun Boolean.plus(other: Boolean): Boolean = true +operator fun Boolean.minus(other: Boolean): Boolean = true +operator fun Boolean.times(other: Boolean): Boolean = true +operator fun Boolean.div(other: Boolean): Boolean = true +operator fun Boolean.rem(other: Boolean): Boolean = true + +operator fun Boolean.rangeTo(other: Boolean): Boolean = true + +fun Boolean.id(): Boolean = true + +operator fun Boolean.inc(): Boolean = true +operator fun Boolean.dec(): Boolean = true + +operator fun Boolean.plusAssign(other: Boolean) {} +operator fun Boolean.minusAssign(other: Boolean) {} +operator fun Boolean.timesAssign(other: Boolean) {} +operator fun Boolean.divAssign(other: Boolean) {} +operator fun Boolean.remAssign(other: Boolean) {} + +operator fun Any?.contains(other: Any): Boolean = false + +fun testWithSubject_ok(x: Boolean, y: Boolean?, any: Any, z: Boolean) { + when { + x.id() -> {} + y?.id() -> {} + any as? Boolean -> {} + any as Boolean -> {} + x * x -> {} + x / x -> {} + x % x -> {} + x + x -> {} + x - x -> {} + x..x -> {} + y -> {} + y ?: x -> {} + x in x -> {} + x !in x -> {} + x is String -> {} + x !is String -> {} + x < x -> {} + x > x -> {} + x <= x -> {} + x >= x -> {} + x == x -> {} + x != x -> {} + x === x -> {} + x !== x -> {} + x && x -> {} + x || x -> {} + } + + var b = z + when (z) { + b++ -> {} + b-- -> {} + } +} diff --git a/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.kt b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.kt new file mode 100644 index 00000000000..c7cfe0a57c9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.kt @@ -0,0 +1,61 @@ +// LANGUAGE: +ProhibitConfusingSyntaxInWhenBranches +// DIAGNOSTICS: -INCOMPATIBLE_TYPES, -NON_EXHAUSTIVE_WHEN_STATEMENT, -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE +// ISSUE: KT-48385 + +operator fun Boolean.plus(other: Boolean): Boolean = true +operator fun Boolean.minus(other: Boolean): Boolean = true +operator fun Boolean.times(other: Boolean): Boolean = true +operator fun Boolean.div(other: Boolean): Boolean = true +operator fun Boolean.rem(other: Boolean): Boolean = true + +operator fun Boolean.rangeTo(other: Boolean): Boolean = true + +fun Boolean.id(): Boolean = true + +operator fun Boolean.inc(): Boolean = true +operator fun Boolean.dec(): Boolean = true + +operator fun Boolean.plusAssign(other: Boolean) {} +operator fun Boolean.minusAssign(other: Boolean) {} +operator fun Boolean.timesAssign(other: Boolean) {} +operator fun Boolean.divAssign(other: Boolean) {} +operator fun Boolean.remAssign(other: Boolean) {} + +operator fun Any?.contains(other: Any): Boolean = false + +fun testWithSubject_ok(x: Boolean, y: Boolean?, any: Any, z: Boolean) { + when { + x.id() -> {} + y?.id() -> {} + any as? Boolean -> {} + any as Boolean -> {} + x * x -> {} + x / x -> {} + x % x -> {} + x + x -> {} + x - x -> {} + x..x -> {} + y -> {} + y ?: x -> {} + x in x -> {} + x !in x -> {} + x is String -> {} + x !is String -> {} + x < x -> {} + x > x -> {} + x <= x -> {} + x >= x -> {} + x == x -> {} + x != x -> {} + x === x -> {} + x !== x -> {} + x && x -> {} + x || x -> {} + } + + var b = z + when (z) { + b++ -> {} + b-- -> {} + } +} diff --git a/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.txt b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.txt new file mode 100644 index 00000000000..c85c3bc277d --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.txt @@ -0,0 +1,19 @@ +package + +public fun testWithSubject_ok(/*0*/ x: kotlin.Boolean, /*1*/ y: kotlin.Boolean?, /*2*/ any: kotlin.Any, /*3*/ z: kotlin.Boolean): kotlin.Unit +public operator fun kotlin.Any?.contains(/*0*/ other: kotlin.Any): kotlin.Boolean +public operator fun kotlin.Boolean.dec(): kotlin.Boolean +public operator fun kotlin.Boolean.div(/*0*/ other: kotlin.Boolean): kotlin.Boolean +public operator fun kotlin.Boolean.divAssign(/*0*/ other: kotlin.Boolean): kotlin.Unit +public fun kotlin.Boolean.id(): kotlin.Boolean +public operator fun kotlin.Boolean.inc(): kotlin.Boolean +public operator fun kotlin.Boolean.minus(/*0*/ other: kotlin.Boolean): kotlin.Boolean +public operator fun kotlin.Boolean.minusAssign(/*0*/ other: kotlin.Boolean): kotlin.Unit +public operator fun kotlin.Boolean.plus(/*0*/ other: kotlin.Boolean): kotlin.Boolean +public operator fun kotlin.Boolean.plusAssign(/*0*/ other: kotlin.Boolean): kotlin.Unit +public operator fun kotlin.Boolean.rangeTo(/*0*/ other: kotlin.Boolean): kotlin.Boolean +public operator fun kotlin.Boolean.rem(/*0*/ other: kotlin.Boolean): kotlin.Boolean +public operator fun kotlin.Boolean.remAssign(/*0*/ other: kotlin.Boolean): kotlin.Unit +public operator fun kotlin.Boolean.times(/*0*/ other: kotlin.Boolean): kotlin.Boolean +public operator fun kotlin.Boolean.timesAssign(/*0*/ other: kotlin.Boolean): kotlin.Unit + diff --git a/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.fir.kt b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.fir.kt new file mode 100644 index 00000000000..96c0e239f4d --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.fir.kt @@ -0,0 +1,236 @@ +// LANGUAGE: +ProhibitConfusingSyntaxInWhenBranches +// DIAGNOSTICS: -INCOMPATIBLE_TYPES, -NON_EXHAUSTIVE_WHEN_STATEMENT, -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE +// ISSUE: KT-48385 + +interface A { + operator fun plus(other: A): A + operator fun minus(other: A): A + operator fun times(other: A): A + operator fun div(other: A): A + operator fun rem(other: A): A + + operator fun rangeTo(other: A): A + + operator fun compareTo(other: A): Int + + fun id(): A +} + +interface B { + operator fun inc(): B + operator fun dec(): B + + operator fun plusAssign(other: B) + operator fun minusAssign(other: B) + operator fun timesAssign(other: B) + operator fun divAssign(other: B) + operator fun remAssign(other: B) +} + +operator fun Any?.contains(other: Any): Boolean = false + +fun testWithSubject_ok(x: A, y: A?, any: Any, z: B) { + when (x) { + x.id() -> {} + y?.id() -> {} + any as? A -> {} + any as A -> {} + x * x -> {} + x / x -> {} + x % x -> {} + x + x -> {} + x - x -> {} + x..x -> {} + y -> {} + y ?: x -> {} + } + + var b = z + when (z) { + b++ -> {} + b-- -> {} + } +} + +fun testWithSubject_bad_1(x: A) { + // bad + when (x) { + x in x -> {} + x !in x -> {} + x is String -> {} + x !is String -> {} + x < x -> {} + x > x -> {} + x <= x -> {} + x >= x -> {} + x == x -> {} + x != x -> {} + x === x -> {} + x !== x -> {} + } + // ok + when (x) { + (x in x) -> {} + (x !in x) -> {} + (x is String) -> {} + (x !is String) -> {} + (x < x) -> {} + (x > x) -> {} + (x <= x) -> {} + (x >= x) -> {} + (x == x) -> {} + (x != x) -> {} + (x === x) -> {} + (x !== x) -> {} + } +} + +fun testWithSubject_bad_2(b: Boolean) { + // bad + when (b) { + b && b -> {} + b || b -> {} + } + // ok + when (b) { + (b && b) -> {} + (b || b) -> {} + } +} + +fun testWithSubject_bad_3(a: Array) { + // bad + when (a) { + *a -> {} + } + // also bad + when (a) { + (*a) -> {} + } +} + +fun testWithSubject_bad_4(b: B) { + var x = b + // bad + when (b) { + x = b -> {} + b += b -> {} + b -= b -> {} + b *= b -> {} + b /= b -> {} + b %= b -> {} + } + // also bad + when (b) { + (x = b) -> {} + (b += b) -> {} + (b -= b) -> {} + (b *= b) -> {} + (b /= b) -> {} + (b %= b) -> {} + } +} + +// range + +fun testWithRange_ok(x: A, y: A?, any: Any, z: B) { + when (x) { + in x.id() -> {} + in y?.id() -> {} + in any as? A -> {} + in any as A -> {} + in x * x -> {} + in x / x -> {} + in x % x -> {} + in x + x -> {} + in x - x -> {} + in x..x -> {} + in y -> {} + in y ?: x -> {} + } + + var b = z + when (z) { + in b++ -> {} + in b-- -> {} + } +} + +fun testWithRange_bad_1(x: A) { + // bad + when (x) { + in x in x -> {} + in x !in x -> {} + in x is String -> {} + in x !is String -> {} + in x < x -> {} + in x > x -> {} + in x <= x -> {} + in x >= x -> {} + in x == x -> {} + in x != x -> {} + in x === x -> {} + in x !== x -> {} + } + // ok + when (x) { + in (x in x) -> {} + in (x !in x) -> {} + in (x is String) -> {} + in (x !is String) -> {} + in (x < x) -> {} + in (x > x) -> {} + in (x <= x) -> {} + in (x >= x) -> {} + in (x == x) -> {} + in (x != x) -> {} + in (x === x) -> {} + in (x !== x) -> {} + } +} + +fun testWithRange_bad_2(b: Boolean) { + // bad + when (b) { + in b && b -> {} + in b || b -> {} + } + // ok + when (b) { + in (b && b) -> {} + in (b || b) -> {} + } +} + +fun testWithRange_bad_3(a: Array) { + // bad + when (a) { + in *a -> {} + } + // also bad + when (a) { + in (*a) -> {} + } +} + +fun testWithRange_bad_4(b: B) { + var x = b + // bad + when (b) { + in x = b -> {} + in b += b -> {} + in b -= b -> {} + in b *= b -> {} + in b /= b -> {} + in b %= b -> {} + } + // also bad + when (b) { + in (x = b) -> {} + in (b += b) -> {} + in (b -= b) -> {} + in (b *= b) -> {} + in (b /= b) -> {} + in (b %= b) -> {} + } +} diff --git a/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.kt b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.kt new file mode 100644 index 00000000000..ec9eebe43bd --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.kt @@ -0,0 +1,236 @@ +// LANGUAGE: +ProhibitConfusingSyntaxInWhenBranches +// DIAGNOSTICS: -INCOMPATIBLE_TYPES, -NON_EXHAUSTIVE_WHEN_STATEMENT, -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE +// ISSUE: KT-48385 + +interface A { + operator fun plus(other: A): A + operator fun minus(other: A): A + operator fun times(other: A): A + operator fun div(other: A): A + operator fun rem(other: A): A + + operator fun rangeTo(other: A): A + + operator fun compareTo(other: A): Int + + fun id(): A +} + +interface B { + operator fun inc(): B + operator fun dec(): B + + operator fun plusAssign(other: B) + operator fun minusAssign(other: B) + operator fun timesAssign(other: B) + operator fun divAssign(other: B) + operator fun remAssign(other: B) +} + +operator fun Any?.contains(other: Any): Boolean = false + +fun testWithSubject_ok(x: A, y: A?, any: Any, z: B) { + when (x) { + x.id() -> {} + y?.id() -> {} + any as? A -> {} + any as A -> {} + x * x -> {} + x / x -> {} + x % x -> {} + x + x -> {} + x - x -> {} + x..x -> {} + y -> {} + y ?: x -> {} + } + + var b = z + when (z) { + b++ -> {} + b-- -> {} + } +} + +fun testWithSubject_bad_1(x: A) { + // bad + when (x) { + x in x -> {} + x !in x -> {} + x is String -> {} + x !is String -> {} + x < x -> {} + x > x -> {} + x <= x -> {} + x >= x -> {} + x == x -> {} + x != x -> {} + x === x -> {} + x !== x -> {} + } + // ok + when (x) { + (x in x) -> {} + (x !in x) -> {} + (x is String) -> {} + (x !is String) -> {} + (x < x) -> {} + (x > x) -> {} + (x <= x) -> {} + (x >= x) -> {} + (x == x) -> {} + (x != x) -> {} + (x === x) -> {} + (x !== x) -> {} + } +} + +fun testWithSubject_bad_2(b: Boolean) { + // bad + when (b) { + b && b -> {} + b || b -> {} + } + // ok + when (b) { + (b && b) -> {} + (b || b) -> {} + } +} + +fun testWithSubject_bad_3(a: Array) { + // bad + when (a) { + *a -> {} + } + // also bad + when (a) { + (*a) -> {} + } +} + +fun testWithSubject_bad_4(b: B) { + var x = b + // bad + when (b) { + x = b -> {} + b += b -> {} + b -= b -> {} + b *= b -> {} + b /= b -> {} + b %= b -> {} + } + // also bad + when (b) { + (x = b) -> {} + (b += b) -> {} + (b -= b) -> {} + (b *= b) -> {} + (b /= b) -> {} + (b %= b) -> {} + } +} + +// range + +fun testWithRange_ok(x: A, y: A?, any: Any, z: B) { + when (x) { + in x.id() -> {} + in y?.id() -> {} + in any as? A -> {} + in any as A -> {} + in x * x -> {} + in x / x -> {} + in x % x -> {} + in x + x -> {} + in x - x -> {} + in x..x -> {} + in y -> {} + in y ?: x -> {} + } + + var b = z + when (z) { + in b++ -> {} + in b-- -> {} + } +} + +fun testWithRange_bad_1(x: A) { + // bad + when (x) { + in x in x -> {} + in x !in x -> {} + in x is String -> {} + in x !is String -> {} + in x < x -> {} + in x > x -> {} + in x <= x -> {} + in x >= x -> {} + in x == x -> {} + in x != x -> {} + in x === x -> {} + in x !== x -> {} + } + // ok + when (x) { + in (x in x) -> {} + in (x !in x) -> {} + in (x is String) -> {} + in (x !is String) -> {} + in (x < x) -> {} + in (x > x) -> {} + in (x <= x) -> {} + in (x >= x) -> {} + in (x == x) -> {} + in (x != x) -> {} + in (x === x) -> {} + in (x !== x) -> {} + } +} + +fun testWithRange_bad_2(b: Boolean) { + // bad + when (b) { + in b && b -> {} + in b || b -> {} + } + // ok + when (b) { + in (b && b) -> {} + in (b || b) -> {} + } +} + +fun testWithRange_bad_3(a: Array) { + // bad + when (a) { + in *a -> {} + } + // also bad + when (a) { + in (*a) -> {} + } +} + +fun testWithRange_bad_4(b: B) { + var x = b + // bad + when (b) { + in x = b -> {} + in b += b -> {} + in b -= b -> {} + in b *= b -> {} + in b /= b -> {} + in b %= b -> {} + } + // also bad + when (b) { + in (x = b) -> {} + in (b += b) -> {} + in (b -= b) -> {} + in (b *= b) -> {} + in (b /= b) -> {} + in (b %= b) -> {} + } +} diff --git a/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.txt b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.txt new file mode 100644 index 00000000000..578ef384002 --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.txt @@ -0,0 +1,40 @@ +package + +public fun testWithRange_bad_1(/*0*/ x: A): kotlin.Unit +public fun testWithRange_bad_2(/*0*/ b: kotlin.Boolean): kotlin.Unit +public fun testWithRange_bad_3(/*0*/ a: kotlin.Array): kotlin.Unit +public fun testWithRange_bad_4(/*0*/ b: B): kotlin.Unit +public fun testWithRange_ok(/*0*/ x: A, /*1*/ y: A?, /*2*/ any: kotlin.Any, /*3*/ z: B): kotlin.Unit +public fun testWithSubject_bad_1(/*0*/ x: A): kotlin.Unit +public fun testWithSubject_bad_2(/*0*/ b: kotlin.Boolean): kotlin.Unit +public fun testWithSubject_bad_3(/*0*/ a: kotlin.Array): kotlin.Unit +public fun testWithSubject_bad_4(/*0*/ b: B): kotlin.Unit +public fun testWithSubject_ok(/*0*/ x: A, /*1*/ y: A?, /*2*/ any: kotlin.Any, /*3*/ z: B): kotlin.Unit +public operator fun kotlin.Any?.contains(/*0*/ other: kotlin.Any): kotlin.Boolean + +public interface A { + public abstract operator fun compareTo(/*0*/ other: A): kotlin.Int + public abstract operator fun div(/*0*/ other: A): A + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract fun id(): A + public abstract operator fun minus(/*0*/ other: A): A + public abstract operator fun plus(/*0*/ other: A): A + public abstract operator fun rangeTo(/*0*/ other: A): A + public abstract operator fun rem(/*0*/ other: A): A + public abstract operator fun times(/*0*/ other: A): A + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface B { + public abstract operator fun dec(): B + public abstract operator fun divAssign(/*0*/ other: B): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract operator fun inc(): B + public abstract operator fun minusAssign(/*0*/ other: B): kotlin.Unit + public abstract operator fun plusAssign(/*0*/ other: B): kotlin.Unit + public abstract operator fun remAssign(/*0*/ other: B): kotlin.Unit + public abstract operator fun timesAssign(/*0*/ other: B): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.fir.kt b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.fir.kt new file mode 100644 index 00000000000..19dbfe2c7de --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.fir.kt @@ -0,0 +1,236 @@ +// LANGUAGE: -ProhibitConfusingSyntaxInWhenBranches +// DIAGNOSTICS: -INCOMPATIBLE_TYPES, -NON_EXHAUSTIVE_WHEN_STATEMENT, -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE +// ISSUE: KT-48385 + +interface A { + operator fun plus(other: A): A + operator fun minus(other: A): A + operator fun times(other: A): A + operator fun div(other: A): A + operator fun rem(other: A): A + + operator fun rangeTo(other: A): A + + operator fun compareTo(other: A): Int + + fun id(): A +} + +interface B { + operator fun inc(): B + operator fun dec(): B + + operator fun plusAssign(other: B) + operator fun minusAssign(other: B) + operator fun timesAssign(other: B) + operator fun divAssign(other: B) + operator fun remAssign(other: B) +} + +operator fun Any?.contains(other: Any): Boolean = false + +fun testWithSubject_ok(x: A, y: A?, any: Any, z: B) { + when (x) { + x.id() -> {} + y?.id() -> {} + any as? A -> {} + any as A -> {} + x * x -> {} + x / x -> {} + x % x -> {} + x + x -> {} + x - x -> {} + x..x -> {} + y -> {} + y ?: x -> {} + } + + var b = z + when (z) { + b++ -> {} + b-- -> {} + } +} + +fun testWithSubject_bad_1(x: A) { + // bad + when (x) { + x in x -> {} + x !in x -> {} + x is String -> {} + x !is String -> {} + x < x -> {} + x > x -> {} + x <= x -> {} + x >= x -> {} + x == x -> {} + x != x -> {} + x === x -> {} + x !== x -> {} + } + // ok + when (x) { + (x in x) -> {} + (x !in x) -> {} + (x is String) -> {} + (x !is String) -> {} + (x < x) -> {} + (x > x) -> {} + (x <= x) -> {} + (x >= x) -> {} + (x == x) -> {} + (x != x) -> {} + (x === x) -> {} + (x !== x) -> {} + } +} + +fun testWithSubject_bad_2(b: Boolean) { + // bad + when (b) { + b && b -> {} + b || b -> {} + } + // ok + when (b) { + (b && b) -> {} + (b || b) -> {} + } +} + +fun testWithSubject_bad_3(a: Array) { + // bad + when (a) { + *a -> {} + } + // also bad + when (a) { + (*a) -> {} + } +} + +fun testWithSubject_bad_4(b: B) { + var x = b + // bad + when (b) { + x = b -> {} + b += b -> {} + b -= b -> {} + b *= b -> {} + b /= b -> {} + b %= b -> {} + } + // also bad + when (b) { + (x = b) -> {} + (b += b) -> {} + (b -= b) -> {} + (b *= b) -> {} + (b /= b) -> {} + (b %= b) -> {} + } +} + +// range + +fun testWithRange_ok(x: A, y: A?, any: Any, z: B) { + when (x) { + in x.id() -> {} + in y?.id() -> {} + in any as? A -> {} + in any as A -> {} + in x * x -> {} + in x / x -> {} + in x % x -> {} + in x + x -> {} + in x - x -> {} + in x..x -> {} + in y -> {} + in y ?: x -> {} + } + + var b = z + when (z) { + in b++ -> {} + in b-- -> {} + } +} + +fun testWithRange_bad_1(x: A) { + // bad + when (x) { + in x in x -> {} + in x !in x -> {} + in x is String -> {} + in x !is String -> {} + in x < x -> {} + in x > x -> {} + in x <= x -> {} + in x >= x -> {} + in x == x -> {} + in x != x -> {} + in x === x -> {} + in x !== x -> {} + } + // ok + when (x) { + in (x in x) -> {} + in (x !in x) -> {} + in (x is String) -> {} + in (x !is String) -> {} + in (x < x) -> {} + in (x > x) -> {} + in (x <= x) -> {} + in (x >= x) -> {} + in (x == x) -> {} + in (x != x) -> {} + in (x === x) -> {} + in (x !== x) -> {} + } +} + +fun testWithRange_bad_2(b: Boolean) { + // bad + when (b) { + in b && b -> {} + in b || b -> {} + } + // ok + when (b) { + in (b && b) -> {} + in (b || b) -> {} + } +} + +fun testWithRange_bad_3(a: Array) { + // bad + when (a) { + in *a -> {} + } + // also bad + when (a) { + in (*a) -> {} + } +} + +fun testWithRange_bad_4(b: B) { + var x = b + // bad + when (b) { + in x = b -> {} + in b += b -> {} + in b -= b -> {} + in b *= b -> {} + in b /= b -> {} + in b %= b -> {} + } + // also bad + when (b) { + in (x = b) -> {} + in (b += b) -> {} + in (b -= b) -> {} + in (b *= b) -> {} + in (b /= b) -> {} + in (b %= b) -> {} + } +} diff --git a/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.kt b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.kt new file mode 100644 index 00000000000..e9efdb6afe3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.kt @@ -0,0 +1,236 @@ +// LANGUAGE: -ProhibitConfusingSyntaxInWhenBranches +// DIAGNOSTICS: -INCOMPATIBLE_TYPES, -NON_EXHAUSTIVE_WHEN_STATEMENT, -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE +// ISSUE: KT-48385 + +interface A { + operator fun plus(other: A): A + operator fun minus(other: A): A + operator fun times(other: A): A + operator fun div(other: A): A + operator fun rem(other: A): A + + operator fun rangeTo(other: A): A + + operator fun compareTo(other: A): Int + + fun id(): A +} + +interface B { + operator fun inc(): B + operator fun dec(): B + + operator fun plusAssign(other: B) + operator fun minusAssign(other: B) + operator fun timesAssign(other: B) + operator fun divAssign(other: B) + operator fun remAssign(other: B) +} + +operator fun Any?.contains(other: Any): Boolean = false + +fun testWithSubject_ok(x: A, y: A?, any: Any, z: B) { + when (x) { + x.id() -> {} + y?.id() -> {} + any as? A -> {} + any as A -> {} + x * x -> {} + x / x -> {} + x % x -> {} + x + x -> {} + x - x -> {} + x..x -> {} + y -> {} + y ?: x -> {} + } + + var b = z + when (z) { + b++ -> {} + b-- -> {} + } +} + +fun testWithSubject_bad_1(x: A) { + // bad + when (x) { + x in x -> {} + x !in x -> {} + x is String -> {} + x !is String -> {} + x < x -> {} + x > x -> {} + x <= x -> {} + x >= x -> {} + x == x -> {} + x != x -> {} + x === x -> {} + x !== x -> {} + } + // ok + when (x) { + (x in x) -> {} + (x !in x) -> {} + (x is String) -> {} + (x !is String) -> {} + (x < x) -> {} + (x > x) -> {} + (x <= x) -> {} + (x >= x) -> {} + (x == x) -> {} + (x != x) -> {} + (x === x) -> {} + (x !== x) -> {} + } +} + +fun testWithSubject_bad_2(b: Boolean) { + // bad + when (b) { + b && b -> {} + b || b -> {} + } + // ok + when (b) { + (b && b) -> {} + (b || b) -> {} + } +} + +fun testWithSubject_bad_3(a: Array) { + // bad + when (a) { + *a -> {} + } + // also bad + when (a) { + (*a) -> {} + } +} + +fun testWithSubject_bad_4(b: B) { + var x = b + // bad + when (b) { + x = b -> {} + b += b -> {} + b -= b -> {} + b *= b -> {} + b /= b -> {} + b %= b -> {} + } + // also bad + when (b) { + (x = b) -> {} + (b += b) -> {} + (b -= b) -> {} + (b *= b) -> {} + (b /= b) -> {} + (b %= b) -> {} + } +} + +// range + +fun testWithRange_ok(x: A, y: A?, any: Any, z: B) { + when (x) { + in x.id() -> {} + in y?.id() -> {} + in any as? A -> {} + in any as A -> {} + in x * x -> {} + in x / x -> {} + in x % x -> {} + in x + x -> {} + in x - x -> {} + in x..x -> {} + in y -> {} + in y ?: x -> {} + } + + var b = z + when (z) { + in b++ -> {} + in b-- -> {} + } +} + +fun testWithRange_bad_1(x: A) { + // bad + when (x) { + in x in x -> {} + in x !in x -> {} + in x is String -> {} + in x !is String -> {} + in x < x -> {} + in x > x -> {} + in x <= x -> {} + in x >= x -> {} + in x == x -> {} + in x != x -> {} + in x === x -> {} + in x !== x -> {} + } + // ok + when (x) { + in (x in x) -> {} + in (x !in x) -> {} + in (x is String) -> {} + in (x !is String) -> {} + in (x < x) -> {} + in (x > x) -> {} + in (x <= x) -> {} + in (x >= x) -> {} + in (x == x) -> {} + in (x != x) -> {} + in (x === x) -> {} + in (x !== x) -> {} + } +} + +fun testWithRange_bad_2(b: Boolean) { + // bad + when (b) { + in b && b -> {} + in b || b -> {} + } + // ok + when (b) { + in (b && b) -> {} + in (b || b) -> {} + } +} + +fun testWithRange_bad_3(a: Array) { + // bad + when (a) { + in *a -> {} + } + // also bad + when (a) { + in (*a) -> {} + } +} + +fun testWithRange_bad_4(b: B) { + var x = b + // bad + when (b) { + in x = b -> {} + in b += b -> {} + in b -= b -> {} + in b *= b -> {} + in b /= b -> {} + in b %= b -> {} + } + // also bad + when (b) { + in (x = b) -> {} + in (b += b) -> {} + in (b -= b) -> {} + in (b *= b) -> {} + in (b /= b) -> {} + in (b %= b) -> {} + } +} diff --git a/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.txt b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.txt new file mode 100644 index 00000000000..cc1e35e361d --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.txt @@ -0,0 +1,41 @@ +package + +public fun testWithRange_bad_1(/*0*/ x: A): kotlin.Unit +public fun testWithRange_bad_2(/*0*/ b: kotlin.Boolean): kotlin.Unit +public fun testWithRange_bad_3(/*0*/ a: kotlin.Array): kotlin.Unit +public fun testWithRange_bad_4(/*0*/ b: B): kotlin.Unit +public fun testWithRange_ok(/*0*/ x: A, /*1*/ y: A?, /*2*/ any: kotlin.Any, /*3*/ z: B): kotlin.Unit +public fun testWithSubject_bad_1(/*0*/ x: A): kotlin.Unit +public fun testWithSubject_bad_2(/*0*/ b: kotlin.Boolean): kotlin.Unit +public fun testWithSubject_bad_3(/*0*/ a: kotlin.Array): kotlin.Unit +public fun testWithSubject_bad_4(/*0*/ b: B): kotlin.Unit +public fun testWithSubject_ok(/*0*/ x: A, /*1*/ y: A?, /*2*/ any: kotlin.Any, /*3*/ z: B): kotlin.Unit +public operator fun kotlin.Any?.contains(/*0*/ other: kotlin.Any): kotlin.Boolean + +public interface A { + public abstract operator fun compareTo(/*0*/ other: A): kotlin.Int + public abstract operator fun div(/*0*/ other: A): A + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract fun id(): A + public abstract operator fun minus(/*0*/ other: A): A + public abstract operator fun plus(/*0*/ other: A): A + public abstract operator fun rangeTo(/*0*/ other: A): A + public abstract operator fun rem(/*0*/ other: A): A + public abstract operator fun times(/*0*/ other: A): A + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface B { + public abstract operator fun dec(): B + public abstract operator fun divAssign(/*0*/ other: B): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract operator fun inc(): B + public abstract operator fun minusAssign(/*0*/ other: B): kotlin.Unit + public abstract operator fun plusAssign(/*0*/ other: B): kotlin.Unit + public abstract operator fun remAssign(/*0*/ other: B): kotlin.Unit + public abstract operator fun timesAssign(/*0*/ other: B): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + diff --git a/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.kt b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.kt index 1a72422b5e1..f8069a707a4 100644 --- a/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.kt +++ b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.kt @@ -10,8 +10,8 @@ fun test_0(b: Boolean): String = when (b) { // Deprecated fun test_1(b: Boolean): String = when (b) { - 1 == 1 -> "true" - "" != "" -> "false" + (1 == 1) -> "true" + ("" != "") -> "false" } const val TRUE = true @@ -28,5 +28,5 @@ const val s2 = "s2" // Already not working fun test_3(b: Boolean): String = when(b) { true -> "true" - s1 == s2 -> "false" + (s1 == s2) -> "false" } diff --git a/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.kt b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.kt index 705d8576547..88047b74f0e 100644 --- a/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.kt +++ b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.kt @@ -9,8 +9,8 @@ fun test_0(b: Boolean): String = when (b) { // Deprecated fun test_1(b: Boolean): String = when (b) { - 1 == 1 -> "true" - "" != "" -> "false" + (1 == 1) -> "true" + ("" != "") -> "false" } const val TRUE = true @@ -27,5 +27,5 @@ const val s2 = "s2" // Already not working fun test_3(b: Boolean): String = when(b) { true -> "true" - s1 == s2 -> "false" + (s1 == s2) -> "false" } diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index ddffee95faa..cf011e8c224 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -32191,6 +32191,24 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/when/CommaInWhenConditionWithoutArgument.kt"); } + @Test + @TestMetadata("deprecatedSyntaxInConditionsNoSubject.kt") + public void testDeprecatedSyntaxInConditionsNoSubject() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditionsNoSubject.kt"); + } + + @Test + @TestMetadata("deprecatedSyntaxInConditions_after.kt") + public void testDeprecatedSyntaxInConditions_after() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_after.kt"); + } + + @Test + @TestMetadata("deprecatedSyntaxInConditions_before.kt") + public void testDeprecatedSyntaxInConditions_before() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/deprecatedSyntaxInConditions_before.kt"); + } + @Test @TestMetadata("DuplicatedLabels.kt") public void testDuplicatedLabels() throws Exception { diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/11.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/11.1.kt index a83702fb404..a06dfaa8cec 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/11.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/11.1.kt @@ -29,7 +29,7 @@ fun case_3(value_1: Boolean?): Int = when // TESTCASE NUMBER: 4 fun case_4(value_1: Boolean?): String = when (value_1) { - true && false && ((true || false)) || true && !!!false && !!!true -> "" - true && false && ((true || false)) || true && !!!false -> "" + true && false && ((true || false)) || true && !!!false && !!!true -> "" + true && false && ((true || false)) || true && !!!false -> "" null -> "" } diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/3.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/3.1.kt index fd5d99bde08..ef5984f4f45 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/3.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/3.1.kt @@ -48,6 +48,6 @@ fun case_5(value_1: Boolean): String { // TESTCASE NUMBER: 6 fun case_6(value_1: Boolean): String = when (value_1) { - true && false && ((true || false)) || true && !!!false && !!!true -> "" - true && false && ((true || false)) || true && !!!false -> "" + true && false && ((true || false)) || true && !!!false && !!!true -> "" + true && false && ((true || false)) || true && !!!false -> "" } diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/11.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/11.1.kt index 8a6e7ef69f9..6ef4fbbff75 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/11.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/11.1.kt @@ -19,7 +19,7 @@ fun case_1(value_1: Boolean?): String = when (value_1) { // TESTCASE NUMBER: 2 fun case_2(value_1: Boolean?): String = when (value_1) { - true && false && ((true || false)) || true && !!!false && !!!true -> "" - true && false && ((true || false)) || true && !!!false -> "" + true && false && ((true || false)) || true && !!!false && !!!true -> "" + true && false && ((true || false)) || true && !!!false -> "" null -> "" } diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/3.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/3.1.kt index 81158d23ad0..55fe89eb7e6 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/3.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/3.1.kt @@ -18,6 +18,6 @@ fun case_1(value_1: Boolean): String = when (value_1) { // TESTCASE NUMBER: 2 fun case_2(value_1: Boolean): String = when (value_1) { - true && false && ((true || false)) || true && !!!false && !!!true -> "" - true && false && ((true || false)) || true && !!!false -> "" + true && false && ((true || false)) || true && !!!false && !!!true -> "" + true && false && ((true || false)) || true && !!!false -> "" } diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-5/pos/1.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-5/pos/1.1.kt index 90a6ee5215d..de2ca93c51b 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-5/pos/1.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-5/pos/1.1.kt @@ -58,13 +58,13 @@ fun case_4(value_1: Int, value_2: String, value_3: String) { fun case_5(value_1: Int, value_2: Int, value_3: Boolean?) { when (value_1) { 1 -> when (value_3) { - value_2 > 1000 -> "1" - value_2 > 100 -> "2" + value_2 > 1000 -> "1" + value_2 > 100 -> "2" else -> "3" } 2 -> when (value_3) { - value_2 > 1000 -> "1" - value_2 > 100 -> "2" + value_2 > 1000 -> "1" + value_2 > 100 -> "2" } 3 -> when (value_3) {} 4 -> when (value_3) { @@ -83,8 +83,8 @@ fun case_5(value_1: Int, value_2: Int, value_3: Boolean?) { // TESTCASE NUMBER: 6 fun case_6(value_1: Int, value_2: Int, value_3: Boolean?) = when (value_1) { 1 -> when (value_3) { - value_2 > 1000 -> 1 - value_2 > 100 -> 2 + value_2 > 1000 -> 1 + value_2 > 100 -> 2 else -> 3 } else -> when (value_3) { diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/pos/5.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/pos/5.1.kt index 8d69c842584..ff1a238cdaa 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/pos/5.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/pos/5.1.kt @@ -37,10 +37,10 @@ fun case_3(value_1: Boolean, value_2: Boolean, value_3: Long) { when (value_1) { value_2 -> {} !value_2 -> {} - getBoolean() && value_2 -> {} - getChar() != 'a' -> {} - getList() === getAny() -> {} - value_3 <= 11 -> {} + getBoolean() && value_2 -> {} + getChar() != 'a' -> {} + getList() === getAny() -> {} + value_3 <= 11 -> {} } } diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/pos/5.2.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/pos/5.2.kt index c93d330a0a6..e6fb3dfa97c 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/pos/5.2.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-6/pos/5.2.kt @@ -29,8 +29,8 @@ fun case_2(value_1: Number, value_2: Int) { // TESTCASE NUMBER: 3 fun case_3(value_1: Boolean, value_2: Boolean, value_3: Long) { when (value_1) { - value_2, !value_2, getBoolean() && value_2, getChar() != 'a' -> {} - getList() === getAny(), value_3 <= 11 -> {} + value_2, !value_2, getBoolean() && value_2, getChar() != 'a' -> {} + getList() === getAny(), value_3 <= 11 -> {} } } diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index dbfad9b1cbb..9d876ca965c 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -234,6 +234,7 @@ enum class LanguageFeature( RefineTypeCheckingOnAssignmentsToJavaFields(KOTLIN_1_7), JvmPermittedSubclassesAttributeForSealed(KOTLIN_1_7), ForbidUsingExtensionPropertyTypeParameterInDelegate(KOTLIN_1_7, kind = BUG_FIX), + ProhibitConfusingSyntaxInWhenBranches(KOTLIN_1_7, kind = BUG_FIX), // KT-48385 // Temporarily disabled, see KT-27084/KT-22379 SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX),