Skip to content

Commit

Permalink
ElseCaseInsteadOfExhaustiveWhen config update (#5623)
Browse files Browse the repository at this point in the history
A new `ignoredSubjectTypes` property with an empty default value was introduced. It can be configured with a list of subject types fully qualified names which should be ignored by the `ElseCaseInsteadOfExhaustiveWhen` rule.
  • Loading branch information
mmorozkov committed Jan 19, 2023
1 parent b780c88 commit 7b8c162
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 0 deletions.
1 change: 1 addition & 0 deletions detekt-core/src/main/resources/default-detekt-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ potential-bugs:
- 'java.util.HashMap'
ElseCaseInsteadOfExhaustiveWhen:
active: false
ignoredSubjectTypes: []
EqualsAlwaysReturnsTrueOrFalse:
active: true
EqualsWithHashCodeExist:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.config
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution
import io.gitlab.arturbosch.detekt.rules.fqNameOrNull
import org.jetbrains.kotlin.cfg.WhenChecker
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.resolve.calls.util.getType
Expand Down Expand Up @@ -59,6 +62,12 @@ class ElseCaseInsteadOfExhaustiveWhen(config: Config = Config.empty) : Rule(conf
Debt.FIVE_MINS
)

@Configuration(
"List of fully qualified types which should be ignored for when expressions with a subject. " +
"Example `kotlinx.serialization.json.JsonObject`"
)
private val ignoredSubjectTypes: List<String> by config(emptyList())

override fun visitWhenExpression(whenExpression: KtWhenExpression) {
super.visitWhenExpression(whenExpression)

Expand All @@ -70,6 +79,10 @@ class ElseCaseInsteadOfExhaustiveWhen(config: Config = Config.empty) : Rule(conf
if (whenExpression.elseExpression == null) return

val subjectType = subjectExpression.getType(bindingContext)
if (ignoredSubjectTypes.contains(subjectType?.fqNameOrNull()?.toString())) {
return
}

val isEnumSubject = WhenChecker.getClassDescriptorOfTypeIfEnum(subjectType) != null
val isSealedSubject = isNonExpectedSealedClass(subjectType)
val isBooleanSubject = subjectType?.isBooleanOrNullableBoolean() == true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.gitlab.arturbosch.detekt.rules.bugs

import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest
import io.gitlab.arturbosch.detekt.test.TestConfig
import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext
import io.gitlab.arturbosch.detekt.test.lintWithContext
import org.assertj.core.api.Assertions.assertThat
Expand Down Expand Up @@ -152,6 +153,114 @@ class ElseCaseInsteadOfExhaustiveWhenSpec(private val env: KotlinCoreEnvironment
}
}

@Nested
inner class `Various ignoreSubjectTypes configurations` {

@Test
fun `does not report if _when_ contains _else_ case for ignored _enum_ subject type`() {
val code = """
package com.example
enum class Color {
RED,
GREEN,
BLUE
}
fun whenOnEnumPasses(c: Color) {
when (c) {
Color.BLUE -> {}
Color.GREEN -> {}
else -> {}
}
}
""".trimIndent()
assertThat(
ElseCaseInsteadOfExhaustiveWhen(
TestConfig(mapOf("ignoredSubjectTypes" to listOf("com.example.Color")))
).compileAndLintWithContext(env, code)
).isEmpty()
}

@Test
fun `does not report if _when_ contains _else_ case for ignored _sealed_ subject type`() {
val code = """
package com.example
sealed class Variant {
object VariantA : Variant()
class VariantB : Variant()
object VariantC : Variant()
}
fun whenOnSealedPasses(v: Variant) {
when (v) {
is Variant.VariantA -> {}
is Variant.VariantB -> {}
else -> {}
}
}
""".trimIndent()
assertThat(
ElseCaseInsteadOfExhaustiveWhen(
TestConfig(mapOf("ignoredSubjectTypes" to listOf("com.example.Variant")))
).compileAndLintWithContext(env, code)
).isEmpty()
}

@Test
fun `reports if _when_ contains _else_ case for non-ignored _enum_ subject type`() {
val code = """
package com.example
enum class Color {
RED,
GREEN,
BLUE
}
fun whenOnEnumFails(c: Color) {
when (c) {
Color.BLUE -> {}
Color.GREEN -> {}
else -> {}
}
}
""".trimIndent()
assertThat(
ElseCaseInsteadOfExhaustiveWhen(
TestConfig(mapOf("ignoredSubjectTypes" to listOf("com.example.Class")))
).compileAndLintWithContext(env, code)
).hasSize(1)
}

@Test
fun `reports if _when_ contains _else_ case for non-ignored _sealed_ subject type`() {
val code = """
package com.example
sealed class Variant {
object VariantA : Variant()
class VariantB : Variant()
object VariantC : Variant()
}
fun whenOnSealedPasses(v: Variant) {
when (v) {
is Variant.VariantA -> {}
is Variant.VariantB -> {}
else -> {}
}
}
""".trimIndent()
assertThat(
ElseCaseInsteadOfExhaustiveWhen(
TestConfig(mapOf("ignoredSubjectTypes" to listOf("com.example.Class")))
).compileAndLintWithContext(env, code)
).hasSize(1)
}
}

@Nested
inner class `Expected sealed class` {
@Test
Expand Down

0 comments on commit 7b8c162

Please sign in to comment.