Skip to content

Commit

Permalink
Fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
VitalyVPinchuk committed Mar 8, 2023
1 parent 40647b8 commit 5872284
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 73 deletions.
4 changes: 4 additions & 0 deletions config/detekt/detekt.yml
Expand Up @@ -153,6 +153,10 @@ style:
active: true
singleLine: 'consistent'
multiLine: 'consistent'
BracesOnWhenStatements:
active: false
singleLine: 'necessary'
multiLine: 'necessary'
CanBeNonNullable:
active: true
CascadingCallWrapping:
Expand Down
2 changes: 1 addition & 1 deletion detekt-core/src/main/resources/default-detekt-config.yml
Expand Up @@ -507,7 +507,7 @@ style:
multiLine: 'always'
BracesOnWhenStatements:
active: false
singleLine: 'consistent'
singleLine: 'necessary'
multiLine: 'consistent'
CanBeNonNullable:
active: false
Expand Down
Expand Up @@ -12,26 +12,27 @@ import io.gitlab.arturbosch.detekt.api.internal.Configuration
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtWhenEntry
import org.jetbrains.kotlin.psi.KtWhenExpression

/**
* This rule detects `when` statements which do not comply with the specified policy.
* Keeping braces consistent will improve readability and avoid possible errors.
*
* Single-line `when` statement is:
* a `when` where each of the entries are single-line (has no line breaks `\n`).
* a `when` where each of the branches are single-line (has no line breaks `\n`).
*
* Multi-line `when` statement is:
* a `when` where at least one of the entries is multi-line (has a break line `\n`)
* or has multiple statements.
* a `when` where at least one of the branches is multi-line (has a break line `\n`).
*
* Available options are:
* * `never`: forces no braces on any entry.
_Tip_: this is very strict, it will force a simple expression, like a single function call / expression. Extracting a function for "complex" logic is one way to adhere to this policy.
* * `necessary`: forces no braces on any entry except where necessary for multi-statement entries.
* * `never`: forces no braces on any branch.
* _Tip_: this is very strict, it will force a simple expression, like a single function call / expression.
* Extracting a function for "complex" logic is one way to adhere to this policy.
* * `necessary`: forces no braces on any branch except where necessary for multi-statement branches.
* * `consistent`: ensures that braces are consistent within `when` statement.
* If there are braces on one of the entries, all entries should have it.
* * `always`: forces braces on all entries.
* If there are braces on one of the branches, all branches should have it.
* * `always`: forces braces on all branches.
*
* <noncompliant>
* // singleLine = 'never'
Expand All @@ -50,11 +51,15 @@ import org.jetbrains.kotlin.psi.KtWhenExpression
* when (a) {
* 1 -> { f1() }
* 2 -> f2()
* 3 -> { f3(); f4() }
* }
* // multiLine = 'necessary'
* when (a) {
* 1 -> { f1() }
* 2 -> { f2(); f3() }
* 2 -> {
* f2()
* f3()
* }
* }
*
* // singleLine = 'consistent'
Expand Down Expand Up @@ -163,41 +168,39 @@ class BracesOnWhenStatements(config: Config = Config.empty) : Rule(config) {
)

@Configuration("single-line braces policy")
private val singleLine: BracePolicy by config("consistent") { BracePolicy.getValue(it) }
private val singleLine: BracePolicy by config("necessary") { BracePolicy.getValue(it) }

@Configuration("multi-line braces policy")
private val multiLine: BracePolicy by config("consistent") { BracePolicy.getValue(it) }

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

val branches: List<KtExpression> = walk(expression)
val branches: List<KtWhenEntry> = walk(expression)
validate(branches, policy(expression))
}

private fun walk(expression: KtWhenExpression): List<KtExpression> {
return expression.entries.mapNotNull { it.expression }
}
private fun walk(expression: KtWhenExpression) = expression.entries

private fun validate(branches: List<KtExpression>, policy: BracePolicy) {
private fun validate(branches: List<KtWhenEntry>, policy: BracePolicy) {
val violators = when (policy) {
BracePolicy.Always -> {
list.filter { !hasBraces(it) }
branches.filter { !hasBraces(it.expression) }
}

BracePolicy.Necessary -> {
list.filter { !isMultiStatement(it) && hasBraces(it) }
branches.filter { !isMultiStatement(it.expression) && hasBraces(it.expression) }
}

BracePolicy.Never -> {
list.filter { hasBraces(it) }
branches.filter { hasBraces(it.expression) }
}

BracePolicy.Consistent -> {
val braces = list.count { hasBraces(it) }
val noBraces = list.count { !hasBraces(it) }
val braces = branches.count { hasBraces(it.expression) }
val noBraces = branches.count { !hasBraces(it.expression) }
if (braces != 0 && noBraces != 0) {
list.take(1)
branches.take(1)
} else {
emptyList()
}
Expand All @@ -206,31 +209,29 @@ class BracesOnWhenStatements(config: Config = Config.empty) : Rule(config) {
violators.forEach { report(it, policy) }
}

private fun hasBraces(expression: KtExpression): Boolean =
private fun hasBraces(expression: KtExpression?): Boolean =
expression is KtBlockExpression

private fun KtExpression.parentCandidate(): PsiElement? =
this.parent.parent
private fun KtWhenEntry.parentCandidate(): PsiElement? =
this.parent

private fun isMultiStatement(expression: KtExpression): Boolean =
private fun isMultiStatement(expression: KtExpression?): Boolean =
expression is KtBlockExpression && expression.statements.size > 1

private fun policy(expression: KtWhenExpression): BracePolicy {
val multiLineCandidate = expression.entries.firstOrNull { entry ->
entry.text.substringAfter("->").contains('\n') ||
entry.expression?.let { isMultiStatement(it) } ?: false
entry.text.substringAfter("->").contains('\n')
}
return if (multiLineCandidate != null) multiLine else singleLine
}

private fun report(violator: KtExpression, policy: BracePolicy) {
private fun report(violator: KtWhenEntry, policy: BracePolicy) {
val parent = violator.parentCandidate() as KtWhenExpression
val entries = parent.entries.mapNotNull { it.expression }
val reported = when {
violator in entries && policy == BracePolicy.Consistent -> parent.whenKeyword
violator in entries -> violator
violator in parent.entries && policy == BracePolicy.Consistent -> parent.whenKeyword
violator in parent.entries -> violator.arrow
else -> error("Violating element (${violator.text}) is not part of this 'when' (${parent.text})")
}
} ?: return
val message = when (policy) {
BracePolicy.Always -> "Missing braces on this branch, add them."
BracePolicy.Consistent -> "Inconsistent braces, make sure all branches either have or don't have braces."
Expand Down

0 comments on commit 5872284

Please sign in to comment.