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.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isElseIf
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isElseIf
|
||||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.unwrapBlockOrParenthesis
|
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.*
|
||||||
|
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.getParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
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 KtIfExpression.branch(thenBranch: Boolean) = if (thenBranch) then else `else`
|
||||||
|
|
||||||
private fun KtExpression.constantBooleanValue(context: BindingContext): Boolean? {
|
private fun KtExpression.constantBooleanValue(context: BindingContext): Boolean? {
|
||||||
|
val enumEntriesComparison = enumEntriesComparison()
|
||||||
|
if (enumEntriesComparison != null) {
|
||||||
|
return enumEntriesComparison
|
||||||
|
}
|
||||||
val type = getType(context) ?: return null
|
val type = getType(context) ?: return null
|
||||||
|
|
||||||
val constantValue = ConstantExpressionEvaluator.getConstant(this, context)?.toConstantValue(type)
|
val constantValue = ConstantExpressionEvaluator.getConstant(this, context)?.toConstantValue(type)
|
||||||
return constantValue?.value as? Boolean
|
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) {
|
fun KtExpression.replaceWithBranch(branch: KtExpression, isUsedAsExpression: Boolean, keepBraces: Boolean = false) {
|
||||||
val lastExpression = when {
|
val lastExpression = when {
|
||||||
branch !is KtBlockExpression -> replaced(branch)
|
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");
|
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")
|
@TestMetadata("expression.kt")
|
||||||
public void testExpression() throws Exception {
|
public void testExpression() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/constantConditionIf/expression.kt");
|
runTest("idea/testData/inspectionsLocal/constantConditionIf/expression.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user