Condition of 'if' expression is constant: report enum entries comparison
#KT-28803 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
662c1b6d4a
commit
301b3aad06
@@ -14,7 +14,11 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isElseIf
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.unwrapBlockOrParenthesis
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
@@ -116,12 +120,42 @@ class ConstantConditionIfInspection : AbstractKotlinInspection() {
|
||||
private fun KtIfExpression.branch(thenBranch: Boolean) = if (thenBranch) then else `else`
|
||||
|
||||
private fun KtExpression.constantBooleanValue(context: BindingContext): Boolean? {
|
||||
val enumEntriesComparison = enumEntriesComparison()
|
||||
if (enumEntriesComparison != null) {
|
||||
return enumEntriesComparison
|
||||
}
|
||||
val type = getType(context) ?: return null
|
||||
|
||||
val constantValue = ConstantExpressionEvaluator.getConstant(this, context)?.toConstantValue(type)
|
||||
return constantValue?.value as? Boolean
|
||||
}
|
||||
|
||||
private fun KtExpression.enumEntriesComparison(): Boolean? {
|
||||
if (this !is KtBinaryExpression) return null
|
||||
val leftEnumEntry = left?.enumEntry() ?: return null
|
||||
val rightEnumEntry = right?.enumEntry() ?: return null
|
||||
|
||||
val leftEnum = leftEnumEntry.containingClass() ?: return null
|
||||
val rightEnum = rightEnumEntry.containingClass() ?: return null
|
||||
if (leftEnum != rightEnum) return null
|
||||
|
||||
val enumEntries = leftEnum.body?.getChildrenOfType<KtEnumEntry>() ?: return null
|
||||
val leftIndex = enumEntries.indexOf(leftEnumEntry)
|
||||
val rightIndex = enumEntries.indexOf(rightEnumEntry)
|
||||
return when (operationToken) {
|
||||
KtTokens.EQEQ -> leftIndex == rightIndex
|
||||
KtTokens.GT -> leftIndex > rightIndex
|
||||
KtTokens.GTEQ -> leftIndex >= rightIndex
|
||||
KtTokens.LT -> leftIndex < rightIndex
|
||||
KtTokens.LTEQ -> leftIndex <= rightIndex
|
||||
KtTokens.EXCLEQ -> leftIndex != rightIndex
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtExpression.enumEntry(): KtEnumEntry? {
|
||||
return ((this as? KtDotQualifiedExpression)?.selectorExpression ?: this).mainReference?.resolve() as? KtEnumEntry
|
||||
}
|
||||
|
||||
fun KtExpression.replaceWithBranch(branch: KtExpression, isUsedAsExpression: Boolean, keepBraces: Boolean = false) {
|
||||
val lastExpression = when {
|
||||
branch !is KtBlockExpression -> replaced(branch)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(if (<caret>Enum.A == Enum.A) 1 else 2)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(1)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(if (<caret>test.Enum.A == test.Enum.A) 1 else 2)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(1)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
import test.Enum.*
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(if (<caret>A == A) 1 else 2)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
import test.Enum.*
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(1)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(if (<caret>Enum.B > Enum.A) 1 else 2)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(1)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(if (<caret>Enum.B >= Enum.A) 1 else 2)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(1)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(if (<caret>Enum.B >= Enum.B) 1 else 2)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(1)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(if (<caret>Enum.B < Enum.C) 1 else 2)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(1)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(if (<caret>Enum.B <= Enum.C) 1 else 2)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(1)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(if (<caret>Enum.B <= Enum.B) 1 else 2)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'true'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(1)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'false'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(if (<caret>Enum.A != Enum.A) 1 else 2)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Condition is always 'false'
|
||||
enum class Enum {
|
||||
A, B, C
|
||||
}
|
||||
|
||||
fun foo(x: Int) {}
|
||||
|
||||
fun bar() {
|
||||
foo(2)
|
||||
}
|
||||
+50
@@ -1524,6 +1524,56 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/endWithElseIfNoBraces.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntryEq.kt")
|
||||
public void testEnumEntryEq() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/enumEntryEq.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntryEq2.kt")
|
||||
public void testEnumEntryEq2() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/enumEntryEq2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntryEq3.kt")
|
||||
public void testEnumEntryEq3() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/enumEntryEq3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntryGt.kt")
|
||||
public void testEnumEntryGt() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/enumEntryGt.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntryGtEq.kt")
|
||||
public void testEnumEntryGtEq() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/enumEntryGtEq.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntryGtEq2.kt")
|
||||
public void testEnumEntryGtEq2() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/enumEntryGtEq2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntryLt.kt")
|
||||
public void testEnumEntryLt() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/enumEntryLt.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntryLtEq.kt")
|
||||
public void testEnumEntryLtEq() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/enumEntryLtEq.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntryLtEq2.kt")
|
||||
public void testEnumEntryLtEq2() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/enumEntryLtEq2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntryNotEq.kt")
|
||||
public void testEnumEntryNotEq() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/enumEntryNotEq.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("expression.kt")
|
||||
public void testExpression() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/expression.kt");
|
||||
|
||||
Reference in New Issue
Block a user