From f69c758b89aabc885a8f5dc64fe869739c8582cb Mon Sep 17 00:00:00 2001 From: "Vitaly V. Pinchuk" Date: Fri, 13 Jan 2023 14:30:35 -0400 Subject: [PATCH 01/33] Initial commit --- config/detekt/detekt.yml | 4 + .../main/resources/default-detekt-config.yml | 4 + .../rules/style/BracesOnIfStatements.kt | 136 +++++++ .../detekt/rules/style/StyleGuideProvider.kt | 1 + .../rules/style/BracesOnIfStatementsSpec.kt | 353 ++++++++++++++++++ 5 files changed, 498 insertions(+) create mode 100644 detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt create mode 100644 detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 742626495b3..3fd7bf592ee 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -149,6 +149,10 @@ potential-bugs: active: true style: + BracesOnIfStatements: + active: true + singleLine: 'consistent' + multiLine: 'consistent' CanBeNonNullable: active: true CascadingCallWrapping: diff --git a/detekt-core/src/main/resources/default-detekt-config.yml b/detekt-core/src/main/resources/default-detekt-config.yml index 4874ec93ed6..edd6a69fade 100644 --- a/detekt-core/src/main/resources/default-detekt-config.yml +++ b/detekt-core/src/main/resources/default-detekt-config.yml @@ -500,6 +500,10 @@ style: active: true AlsoCouldBeApply: active: false + BracesOnIfStatements: + active: false + singleLine: 'never' + multiLine: 'always' CanBeNonNullable: active: false CascadingCallWrapping: diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt new file mode 100644 index 00000000000..6ca216c928d --- /dev/null +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt @@ -0,0 +1,136 @@ +package io.gitlab.arturbosch.detekt.rules.style + +import io.gitlab.arturbosch.detekt.api.CodeSmell +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.Debt +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 org.jetbrains.kotlin.com.intellij.psi.PsiElement +import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtIfExpression +import org.jetbrains.kotlin.psi.KtWhenExpression +import org.jetbrains.kotlin.psi.psiUtil.siblings + +/** + * This rule detects `if` statements which do not comply with the specified rules. + * Keeping braces consistent would improve readability and avoid possible errors. + * + * + * val i = 1 + * if (i > 0) + * println(i) + * else { + * println(-i) + * } + * + * + * + * || singleLine = 'never' multiLine = 'always' + * val x = if (condition) 5 else 4 + * val x = if (condition) { + * 5 + * } else { + * 4 + * } + * + */ +class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { + + override val issue = Issue( + javaClass.simpleName, + Severity.Style, + "Braces do not comply with the specified policy", + Debt.FIVE_MINS + ) + + @Configuration("single-line braces policy") + private val singleLine: String by config(BRACES_NEVER) + + @Configuration("multi-line braces policy") + private val multiLine: String by config(BRACES_ALWAYS) + + override fun visitIfExpression(expression: KtIfExpression) { + super.visitIfExpression(expression) + + val isMultiline = isMultiline(expression.ifKeyword) + + val thenExpression = expression.then ?: return + if (!isCompliant(thenExpression, isMultiline)) { + report(CodeSmell(issue, Entity.from(expression.ifKeyword), issue.description)) + } + + val elseExpression = expression.`else` ?: return + if (!shouldSkip(elseExpression) && !isCompliant(elseExpression, isMultiline)) { + report(CodeSmell(issue, Entity.from(expression.elseKeyword ?: elseExpression), issue.description)) + } + + if (!isConsistent(thenExpression, elseExpression, isMultiline)) { + report(CodeSmell(issue, Entity.from(expression.ifKeyword), "Inconsistent braces")) + } + } + + private fun isCompliant(expression: KtExpression, isMultiline: Boolean): Boolean = when { + isMultiStatement(expression) -> { + true + } + + isMultiline && (multiLine == BRACES_ALWAYS) || !isMultiline && (singleLine == BRACES_ALWAYS) -> { + expression is KtBlockExpression + } + + isMultiline && (multiLine == BRACES_NEVER) || !isMultiline && (singleLine == BRACES_NEVER) -> { + expression !is KtBlockExpression + } + + else -> { + true + } + } + + private fun isConsistent( + thenExpression: KtExpression, + elseExpression: KtExpression, + isMultiline: Boolean + ): Boolean = when { + shouldSkip(elseExpression) -> { + true + } + + isMultiline && (multiLine == BRACES_CONSISTENT) || !isMultiline && (singleLine == BRACES_CONSISTENT) -> { + thenExpression is KtBlockExpression && elseExpression is KtBlockExpression || + thenExpression !is KtBlockExpression && elseExpression !is KtBlockExpression + } + + else -> { + true + } + } + + private fun isMultiStatement(expression: KtExpression): Boolean { + if (expression !is KtBlockExpression) return false + + return expression + .firstChild + .siblings(forward = true, withItself = false) + .count { it is KtExpression } > 0 + } + + private fun isMultiline(element: PsiElement?): Boolean { + if (element == null) return false + return element.siblings().any { it.textContains('\n') } + } + + private fun shouldSkip(expression: KtExpression): Boolean = + expression is KtIfExpression || expression is KtWhenExpression + + companion object { + private const val BRACES_ALWAYS = "always" + private const val BRACES_NEVER = "never" + private const val BRACES_CONSISTENT = "consistent" + } +} diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt index 889df2144e2..18aaf120aa1 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt @@ -77,6 +77,7 @@ class StyleGuideProvider : DefaultRuleSetProvider { MayBeConst(config), PreferToOverPairSyntax(config), MandatoryBracesIfStatements(config), + BracesOnIfStatements(config), MandatoryBracesLoops(config), NullableBooleanCheck(config), VarCouldBeVal(config), diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt new file mode 100644 index 00000000000..f9aef2c06c9 --- /dev/null +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -0,0 +1,353 @@ +package io.gitlab.arturbosch.detekt.rules.style + +import io.gitlab.arturbosch.detekt.test.TestConfig +import io.gitlab.arturbosch.detekt.test.assertThat +import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +class BracesOnIfStatementsSpec { + + private fun createSubject(singleLine: String, multiLine: String): BracesOnIfStatements { + val config = TestConfig( + mapOf( + "singleLine" to singleLine, + "multiLine" to multiLine + ) + ) + return BracesOnIfStatements(config) + } + + @Nested + inner class `consistency checks` { + val subject = createSubject("consistent", "consistent") + + @Test + fun `does not report consistent multi-line with else if`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) { + println() + } else if (true) println() + if (true) println() + else if (true) println() + } + """.trimIndent() + ) + + assertThat(findings).isEmpty() + } + + @Test + fun `does not report consistent single-line with else if`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) { println() } else if (true) println() + if (true) { println() } else { if (true) println() } + if (true) println() else if (true) println() + } + """.trimIndent() + ) + + assertThat(findings).isEmpty() + } + + @Test + fun `does not report consistent single line if`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) println() else println() + if (true) { println() } else { println() } + } + """.trimIndent() + ) + + assertThat(findings).isEmpty() + } + + @Test + fun `does not report consistent multi-line if`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) { + println() + } else { + println() + } + if (true) println() + else println() + } + """.trimIndent() + ) + + assertThat(findings).isEmpty() + } + + @Test + fun `reports inconsistent single line if`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) { println() } else println() + if (true) println() else { println() } + } + """.trimIndent() + ) + + assertThat(findings).hasSize(2) + assertThat(findings).hasTextLocations(14 to 16, 57 to 59) + } + + @Test + fun `reports inconsistent multi line if`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) { + println() + } else println() + if (true) + println() + else { println() } + } + """.trimIndent() + ) + + assertThat(findings).hasSize(2) + assertThat(findings).hasTextLocations(14 to 16, 70 to 72) + } + } + + @Nested + inner class `if statements which should have braces` { + val subject = createSubject("never", "always") + + @Test + fun `reports either then or else without braces`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) { println() } + else println() + + if (true) println() + else { println() } + } + """.trimIndent() + ) + + assertThat(findings).hasSize(2) + assertThat(findings).hasTextLocations(43 to 47, 67 to 69) + } + + @Test + fun `reports a simple if`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) + println() + } + """.trimIndent() + ) + + assertThat(findings).hasSize(1) + assertThat(findings).hasTextLocations(14 to 16) + } + + @Test + fun `reports a simple if with a single statement in multiple lines`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) 50 + .toString() + } + """.trimIndent() + ) + + assertThat(findings).hasSize(1) + } + + @Test + fun `reports if-else with a single statement in multiple lines`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) 50 + .toString() else 50 + .toString() + } + """.trimIndent() + ) + + assertThat(findings).hasSize(2) + assertThat(findings).hasTextLocations(11 to 13, 44 to 48) + } + + @Test + fun `reports if-else`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) + println() + else + println() + } + """.trimIndent() + ) + + assertThat(findings).hasSize(2) + assertThat(findings).hasTextLocations(14 to 16, 46 to 50) + } + + @Test + fun `reports if-else with else-if`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) + println() + else if (false) + println() + else + println() + } + """.trimIndent() + ) + + assertThat(findings).hasSize(3) + assertThat(findings).hasTextLocations(14 to 16, 51 to 53, 84 to 88) + } + + @Test + fun `reports if with braces but else without`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) { + println() + } else + println() + } + """.trimIndent() + ) + + assertThat(findings).hasSize(1) + assertThat(findings).hasTextLocations(50 to 54) + } + + @Test + fun `reports else with braces but if without`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) + println() + else { + println() + } + } + """.trimIndent() + ) + + assertThat(findings).hasSize(1) + assertThat(findings).hasTextLocations(14 to 16) + } + + @Test + fun `reports both then and else 1`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) println() + else println() + } + """.trimIndent() + ) + + assertThat(findings).hasSize(2) + assertThat(findings).hasTextLocations(14 to 16, 38 to 42) + } + + @Test + fun `reports both then and else 2`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) println() else + println() + } + """.trimIndent() + ) + + assertThat(findings).hasSize(2) + assertThat(findings).hasTextLocations(14 to 16, 34 to 38) + } + } + + @Nested + inner class `multiline if statements with braces` { + val subject = createSubject("never", "always") + + @Test + fun `does not report if statements with braces`() { + val code = """ + fun f() { + if (true) { + println() + } + if (true) + { + println() + } + if (true) + { println() } + } + """.trimIndent() + assertThat(subject.compileAndLint(code)).isEmpty() + } + } + + @Nested + inner class `single-line if statements which don't need braces` { + val subject = createSubject("never", "always") + + @Test + fun `does not report single-line if statements`() { + val code = """ + fun f() { + if (true) println() + if (true) println() else println() + if (true) println() else if (false) println() else println() + } + """.trimIndent() + assertThat(subject.compileAndLint(code)).isEmpty() + } + } + + @Nested + inner class `multi-line when following an else statement without requiring braces` { + val subject = createSubject("never", "always") + + @Test + fun `does not report multi-line when`() { + val code = """ + fun f(i: Int) { + if (true) { + println() + } else when(i) { + 1 -> println(1) + else -> println() + } + } + """.trimIndent() + assertThat(subject.compileAndLint(code)).isEmpty() + } + } +} From 1ca4daaab825512a71838a908cb9e2adfddee865 Mon Sep 17 00:00:00 2001 From: "Vitaly V. Pinchuk" Date: Tue, 17 Jan 2023 15:23:58 -0400 Subject: [PATCH 02/33] Rework --- .../main/resources/default-detekt-config.yml | 2 - .../src/main/resources/deprecation.properties | 1 + .../rules/style/BracesOnIfStatements.kt | 162 +++++++++++------- .../optional/MandatoryBracesIfStatements.kt | 4 + .../rules/style/BracesOnIfStatementsSpec.kt | 4 +- 5 files changed, 105 insertions(+), 68 deletions(-) diff --git a/detekt-core/src/main/resources/default-detekt-config.yml b/detekt-core/src/main/resources/default-detekt-config.yml index edd6a69fade..559029aa147 100644 --- a/detekt-core/src/main/resources/default-detekt-config.yml +++ b/detekt-core/src/main/resources/default-detekt-config.yml @@ -602,8 +602,6 @@ style: ignoreEnums: false ignoreRanges: false ignoreExtensionFunctions: true - MandatoryBracesIfStatements: - active: false MandatoryBracesLoops: active: false MaxChainedCallsOnSameLine: diff --git a/detekt-core/src/main/resources/deprecation.properties b/detekt-core/src/main/resources/deprecation.properties index 5175f2ca810..5bfaf9b2a38 100644 --- a/detekt-core/src/main/resources/deprecation.properties +++ b/detekt-core/src/main/resources/deprecation.properties @@ -8,6 +8,7 @@ potential-bugs>RedundantElseInWhen=Rule deprecated as compiler performs this che naming>FunctionParameterNaming>ignoreOverriddenFunctions=Use `ignoreOverridden` instead naming>MemberNameEqualsClassName>ignoreOverriddenFunction=Use `ignoreOverridden` instead style>FunctionOnlyReturningConstant>excludeAnnotatedFunction=Use `ignoreAnnotated` instead +style>MandatoryBracesIfStatements=Use 'BracesOnIfStatements()' instead style>UnderscoresInNumericLiterals>acceptableDecimalLength=Use `acceptableLength` instead style>UnnecessaryAbstractClass>excludeAnnotatedClasses=Use `ignoreAnnotated` instead style>UseDataClass>excludeAnnotatedClasses=Use `ignoreAnnotated` instead diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt index 6ca216c928d..0af6aa59142 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt @@ -9,34 +9,68 @@ 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 org.jetbrains.kotlin.com.intellij.psi.PsiElement import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtWhenExpression -import org.jetbrains.kotlin.psi.psiUtil.siblings /** * This rule detects `if` statements which do not comply with the specified rules. * Keeping braces consistent would improve readability and avoid possible errors. * + * SingleLine if-statement has no '\n': + * if (a) b else c + * MultiLine if-statement has at least one '\n': + * if (a) b + * else c + * * - * val i = 1 - * if (i > 0) - * println(i) - * else { - * println(-i) - * } + * // singleLine = 'never' + * if (a) { b } else { c } + * if (a) { b } else c + * // multiLine = 'never' + * if (a) { b } + * else { c } + * + * // singleLine = 'always' + * if (a) b else c + * if (a) { b } else c + * // multiLine = 'always' + * if (a) { b } + * else c + * + * // singleLine = 'consistent' + * if (a) b else { c } + * // multiLine = 'consistent' + * if (a) b + * else { c } * * * - * || singleLine = 'never' multiLine = 'always' - * val x = if (condition) 5 else 4 - * val x = if (condition) { - * 5 - * } else { - * 4 - * } + * // singleLine = 'never' + * if (a) b else c + * if (a) b else { c; d; } // multi-expression + * // multiLine = 'never' + * if (a) b + * else c + * + * // singleLine = 'always' + * if (a) { b } else { c } + * if (a) { b } else if (c) { d } + * // multiLine = 'always' + * if (a) { b } + * else { c } + * if (a) { b } + * else if (c) { d } + * + * // singleLine = 'consistent' + * if (a) { b } else { c } + * if (a) b else { c; d } // multi-expression + * // multiLine = 'consistent' + * if (a) { b } + * else { c } + * if (a) b + * else c * */ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { @@ -49,88 +83,88 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { ) @Configuration("single-line braces policy") - private val singleLine: String by config(BRACES_NEVER) + private val singleLine: BracePolicy by config(BRACES_NEVER) { BracePolicy.getValue(it) } @Configuration("multi-line braces policy") - private val multiLine: String by config(BRACES_ALWAYS) + private val multiLine: BracePolicy by config(BRACES_ALWAYS) { BracePolicy.getValue(it) } override fun visitIfExpression(expression: KtIfExpression) { super.visitIfExpression(expression) - val isMultiline = isMultiline(expression.ifKeyword) + val policy = policy(expression) val thenExpression = expression.then ?: return - if (!isCompliant(thenExpression, isMultiline)) { + if (!isExpressionCompliant(policy, thenExpression)) { report(CodeSmell(issue, Entity.from(expression.ifKeyword), issue.description)) } val elseExpression = expression.`else` ?: return - if (!shouldSkip(elseExpression) && !isCompliant(elseExpression, isMultiline)) { + if (!shouldSkipElse(elseExpression) && !isExpressionCompliant(policy, elseExpression)) { report(CodeSmell(issue, Entity.from(expression.elseKeyword ?: elseExpression), issue.description)) } - if (!isConsistent(thenExpression, elseExpression, isMultiline)) { + if (!isStatementConsistent(policy, thenExpression, elseExpression)) { report(CodeSmell(issue, Entity.from(expression.ifKeyword), "Inconsistent braces")) } } - private fun isCompliant(expression: KtExpression, isMultiline: Boolean): Boolean = when { - isMultiStatement(expression) -> { - true - } + private fun policy(expression: KtExpression) = if (expression.textContains('\n')) multiLine else singleLine - isMultiline && (multiLine == BRACES_ALWAYS) || !isMultiline && (singleLine == BRACES_ALWAYS) -> { - expression is KtBlockExpression - } + private fun hasBraces(expression: KtExpression) = expression is KtBlockExpression - isMultiline && (multiLine == BRACES_NEVER) || !isMultiline && (singleLine == BRACES_NEVER) -> { - expression !is KtBlockExpression - } + private fun isExpressionCompliant(policy: BracePolicy, expression: KtExpression): Boolean { + if (isMultiStatement(expression)) return true - else -> { - true + return when (policy) { + BracePolicy.Always -> { hasBraces(expression) } + BracePolicy.Never -> { !hasBraces(expression) } + BracePolicy.Consistent -> { true } } } - private fun isConsistent( + private fun isStatementConsistent( + policy: BracePolicy, thenExpression: KtExpression, - elseExpression: KtExpression, - isMultiline: Boolean - ): Boolean = when { - shouldSkip(elseExpression) -> { - true - } - - isMultiline && (multiLine == BRACES_CONSISTENT) || !isMultiline && (singleLine == BRACES_CONSISTENT) -> { - thenExpression is KtBlockExpression && elseExpression is KtBlockExpression || - thenExpression !is KtBlockExpression && elseExpression !is KtBlockExpression - } - - else -> { - true - } - } - - private fun isMultiStatement(expression: KtExpression): Boolean { - if (expression !is KtBlockExpression) return false - - return expression - .firstChild - .siblings(forward = true, withItself = false) - .count { it is KtExpression } > 0 + elseExpression: KtExpression + ): Boolean { + if (shouldSkipElse(elseExpression) || + isMultiStatement(thenExpression) || + isMultiStatement(elseExpression) + ) { return true } + + return if (policy == BracePolicy.Consistent) { + hasBraces(thenExpression) && hasBraces(elseExpression) || + !hasBraces(thenExpression) && !hasBraces(elseExpression) + } else { true } } - private fun isMultiline(element: PsiElement?): Boolean { - if (element == null) return false - return element.siblings().any { it.textContains('\n') } - } + private fun isMultiStatement(expression: KtExpression): Boolean = + expression is KtBlockExpression && expression.statements.size > 1 - private fun shouldSkip(expression: KtExpression): Boolean = - expression is KtIfExpression || expression is KtWhenExpression + private fun shouldSkipElse(elseExpression: KtExpression): Boolean = + elseExpression is KtIfExpression || elseExpression is KtWhenExpression companion object { private const val BRACES_ALWAYS = "always" private const val BRACES_NEVER = "never" private const val BRACES_CONSISTENT = "consistent" } + + enum class BracePolicy { + Always, + Never, + Consistent; + + companion object { + fun getValue(arg: String) = + when (arg) { + BRACES_NEVER -> Never + BRACES_ALWAYS -> Always + BRACES_CONSISTENT -> Consistent + else -> error( + "Unknown value $arg, allowed values are: $BRACES_NEVER|$BRACES_ALWAYS|$BRACES_CONSISTENT" + ) + } + } + } } diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt index c189217d140..dfaa22d9309 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt @@ -28,6 +28,10 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings * val x = if (condition) 5 else 4 * */ +@Deprecated( + "Use 'BracesOnIfStatements()' instead", + ReplaceWith("BracesOnIfStatements(config)") +) class MandatoryBracesIfStatements(config: Config = Config.empty) : Rule(config) { override val issue = Issue( diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index f9aef2c06c9..39eb5819bb3 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -23,7 +23,7 @@ class BracesOnIfStatementsSpec { val subject = createSubject("consistent", "consistent") @Test - fun `does not report consistent multi-line with else if`() { + fun `does not report multi-line with else if`() { val findings = subject.compileAndLint( """ fun f() { @@ -40,7 +40,7 @@ class BracesOnIfStatementsSpec { } @Test - fun `does not report consistent single-line with else if`() { + fun `does not report single-line with else if`() { val findings = subject.compileAndLint( """ fun f() { From 8f4d25b8f8e0808a060aec9e010d70e591855056 Mon Sep 17 00:00:00 2001 From: "Vitaly V. Pinchuk" Date: Wed, 18 Jan 2023 18:12:41 -0400 Subject: [PATCH 03/33] Rework --- .../rules/style/BracesOnIfStatements.kt | 90 ++-- .../rules/style/BracesOnIfStatementsSpec.kt | 510 ++++++++---------- 2 files changed, 278 insertions(+), 322 deletions(-) diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt index 0af6aa59142..6d41b4ec4ee 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt @@ -12,7 +12,6 @@ import io.gitlab.arturbosch.detekt.api.internal.Configuration import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtIfExpression -import org.jetbrains.kotlin.psi.KtWhenExpression /** * This rule detects `if` statements which do not comply with the specified rules. @@ -90,59 +89,68 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { override fun visitIfExpression(expression: KtIfExpression) { super.visitIfExpression(expression) + if (expression.parent.parent is KtIfExpression) return - val policy = policy(expression) - - val thenExpression = expression.then ?: return - if (!isExpressionCompliant(policy, thenExpression)) { - report(CodeSmell(issue, Entity.from(expression.ifKeyword), issue.description)) - } - - val elseExpression = expression.`else` ?: return - if (!shouldSkipElse(elseExpression) && !isExpressionCompliant(policy, elseExpression)) { - report(CodeSmell(issue, Entity.from(expression.elseKeyword ?: elseExpression), issue.description)) - } + val branches: List = walk(expression) + validate(branches, policy(expression)) + } - if (!isStatementConsistent(policy, thenExpression, elseExpression)) { - report(CodeSmell(issue, Entity.from(expression.ifKeyword), "Inconsistent braces")) + private fun walk(expression: KtExpression): List { + val list = mutableListOf() + var current: KtExpression? = expression + while (current is KtIfExpression) { + current.then?.let { list.add(it) } + // Don't add `if` because it's an `else if` which we treat as one unit. + current.`else`?.takeIf { it !is KtIfExpression }?.let { list.add(it) } + current = current.`else` } + return list } - private fun policy(expression: KtExpression) = if (expression.textContains('\n')) multiLine else singleLine - - private fun hasBraces(expression: KtExpression) = expression is KtBlockExpression + private fun validate(list: List, policy: BracePolicy) { + when (policy) { + BracePolicy.Always -> { + if (!list.all { hasBraces(it) }) { + val violator = list.first { !hasBraces(it) } + report(violator, policy) + } + } - private fun isExpressionCompliant(policy: BracePolicy, expression: KtExpression): Boolean { - if (isMultiStatement(expression)) return true + BracePolicy.Never -> { + if (!list.all { !hasBraces(it) }) { + val violator = list.first { hasBraces(it) } + report(violator, policy) + } + } - return when (policy) { - BracePolicy.Always -> { hasBraces(expression) } - BracePolicy.Never -> { !hasBraces(expression) } - BracePolicy.Consistent -> { true } + BracePolicy.Consistent -> { + val reference = hasBraces(list.first()) + if (!list.all { hasBraces(it) == reference }) { + val violator = list.first { hasBraces(it) != reference } + report(violator, policy) + } + } } } - private fun isStatementConsistent( - policy: BracePolicy, - thenExpression: KtExpression, - elseExpression: KtExpression - ): Boolean { - if (shouldSkipElse(elseExpression) || - isMultiStatement(thenExpression) || - isMultiStatement(elseExpression) - ) { return true } - - return if (policy == BracePolicy.Consistent) { - hasBraces(thenExpression) && hasBraces(elseExpression) || - !hasBraces(thenExpression) && !hasBraces(elseExpression) - } else { true } + private fun report(violator: KtExpression, policy: BracePolicy) { + val iff = violator.parent.parent as KtIfExpression + val reported = when { + iff.then === violator -> iff.ifKeyword + iff.`else` === violator && isMultiStatement(violator) -> iff.ifKeyword + iff.`else` === violator -> iff.elseKeyword + else -> error("Violating element ($violator) is not part of this if: $iff") + } + report(CodeSmell(issue, Entity.from(reported ?: violator), issue.description + ": $policy")) } - private fun isMultiStatement(expression: KtExpression): Boolean = + private fun isMultiStatement(expression: KtExpression) = expression is KtBlockExpression && expression.statements.size > 1 - private fun shouldSkipElse(elseExpression: KtExpression): Boolean = - elseExpression is KtIfExpression || elseExpression is KtWhenExpression + private fun policy(expression: KtExpression): BracePolicy = + if (expression.textContains('\n')) multiLine else singleLine + + private fun hasBraces(expression: KtExpression): Boolean = expression is KtBlockExpression companion object { private const val BRACES_ALWAYS = "always" @@ -156,7 +164,7 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { Consistent; companion object { - fun getValue(arg: String) = + fun getValue(arg: String): BracePolicy = when (arg) { BRACES_NEVER -> Never BRACES_ALWAYS -> Always diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 39eb5819bb3..b7867bd59ae 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -3,9 +3,163 @@ package io.gitlab.arturbosch.detekt.rules.style import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.intellij.lang.annotations.Language import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test +@Language("kotlin") +private const val BLOCK_WITHOUT_BRACES_PASS = """ + fun f() { + if (true) println() + if (true) + println() + if (true) println() else println() + if (true) + println() + else + println() + + if (true) println() else if (true) println() else println() + if (true) + println() + else + if (true) println() else println() + + val a = 0 + when (a) { + 0 -> if (true) println() else println() + 1 -> if (true) + println() + else + println() + 2 -> if (true) println() else if (true) println() else println() + 3 -> if (true) + println() + else + if (true) println() else println() + } + } +""" + +@Language("kotlin") +private const val BLOCK_WITH_BRACES_PASS = """ + fun f() { + if (true) { println() } + if (true) { + println() + } + if (true) { println() } else { println() } + if (true) { + println() + } else { + println() + } + + if (true) { println() } else if (true) { println() } else { println() } + if (true) { println() } else { if (true) { println() } else { println() } } + if (true) { + println() + } else if (true) { + println() + } else { + println() + } + + val a = 0 + when (a) { + 0 -> if (true) { println() } else { println() } + 1 -> if (true) { + println() + } else { + println() + } + 2 -> if (true) { println() } else if (true) { println() } else { println() } + 3 -> if (true) { + println() + } else if (true) { + println() + } else { + println() + } + } + } +""" + +@Language("kotlin") +private const val BLOCK_WITHOUT_BRACES_FAIL = """ + fun f() { + if (true) println() + if (true) + println() + if (true) { println() } else println() + if (true) + println() + else { + println() + } + + if (true) println() else if (true) println() else { println() } + if (true) + println() + else + if (true) { println() } else println() + + val a = 0 + when (a) { + 0 -> if (true) println() else { println() } + 1 -> if (true) + { println() } + else + println() + 2 -> if (true) println() else if (true) { println() } else println() + 3 -> if (true) + println() + else + if (true) println() else { println() } + } + } +""" + +@Language("kotlin") +private const val BLOCK_WITH_BRACES_FAIL = """ + fun f() { + if (true) { println() } + if (true) { + println() + } + if (true) println() else { println() } + if (true) { + println() + } else println() + + if (true) { println() } else if (true) println() else { println() } + if (true) println() else { if (true) println() else println() } + if (true) { + println() + } else if (true) { + println() + } else + println() + + val a = 0 + when (a) { + 0 -> if (true) { println() } else println() + 1 -> if (true) println() + else { + println() + } + 2 -> if (true) { println() } else if (true) { println() } else println() + 3 -> if (true) { + println() + } else if (true) + println() + else { + println() + } + } + } +""" + class BracesOnIfStatementsSpec { private fun createSubject(singleLine: String, multiLine: String): BracesOnIfStatements { @@ -19,335 +173,129 @@ class BracesOnIfStatementsSpec { } @Nested - inner class `consistency checks` { + inner class `braces consistent` { val subject = createSubject("consistent", "consistent") @Test - fun `does not report multi-line with else if`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) { - println() - } else if (true) println() - if (true) println() - else if (true) println() - } - """.trimIndent() - ) + fun `does not report consistent block without braces`() { + val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_PASS) assertThat(findings).isEmpty() } @Test - fun `does not report single-line with else if`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) { println() } else if (true) println() - if (true) { println() } else { if (true) println() } - if (true) println() else if (true) println() - } - """.trimIndent() - ) + fun `does not report consistent block with braces`() { + val findings = subject.compileAndLint(BLOCK_WITH_BRACES_PASS) assertThat(findings).isEmpty() } @Test - fun `does not report consistent single line if`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) println() else println() - if (true) { println() } else { println() } - } - """.trimIndent() + fun `reports inconsistent block without braces`() { + val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_FAIL) + + assertThat(findings).hasSize(8) + assertThat(findings).hasTextLocations( + 119 to 123, + 185 to 189, + 278 to 282, + 365 to 367, + 479 to 483, + 577 to 581, + 655 to 657, + 815 to 819 ) - - assertThat(findings).isEmpty() } @Test - fun `does not report consistent multi-line if`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) { - println() - } else { - println() - } - if (true) println() - else println() - } - """.trimIndent() + fun `reports inconsistent block with braces`() { + val findings = subject.compileAndLint(BLOCK_WITH_BRACES_FAIL) + + assertThat(findings).hasSize(9) + assertThat(findings).hasTextLocations( + 129 to 133, + 201 to 205, + 254 to 256, + 321 to 325, + 469 to 473, + 576 to 580, + 644 to 648, + 770 to 774, + 867 to 869 ) - - assertThat(findings).isEmpty() - } - - @Test - fun `reports inconsistent single line if`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) { println() } else println() - if (true) println() else { println() } - } - """.trimIndent() - ) - - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(14 to 16, 57 to 59) } @Test - fun `reports inconsistent multi line if`() { + fun `reports if when else is multi-statement`() { val findings = subject.compileAndLint( """ fun f() { - if (true) { - println() - } else println() - if (true) - println() - else { println() } + if (true) println() else { println(); println() } } """.trimIndent() ) - - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(14 to 16, 70 to 72) - } - } - - @Nested - inner class `if statements which should have braces` { - val subject = createSubject("never", "always") - - @Test - fun `reports either then or else without braces`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) { println() } - else println() - - if (true) println() - else { println() } - } - """.trimIndent() - ) - - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(43 to 47, 67 to 69) - } - - @Test - fun `reports a simple if`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) - println() - } - """.trimIndent() - ) - assertThat(findings).hasSize(1) assertThat(findings).hasTextLocations(14 to 16) } + } - @Test - fun `reports a simple if with a single statement in multiple lines`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) 50 - .toString() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - } - - @Test - fun `reports if-else with a single statement in multiple lines`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) 50 - .toString() else 50 - .toString() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(11 to 13, 44 to 48) - } - - @Test - fun `reports if-else`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) - println() - else - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(14 to 16, 46 to 50) - } - - @Test - fun `reports if-else with else-if`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) - println() - else if (false) - println() - else - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(3) - assertThat(findings).hasTextLocations(14 to 16, 51 to 53, 84 to 88) - } - - @Test - fun `reports if with braces but else without`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) { - println() - } else - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - assertThat(findings).hasTextLocations(50 to 54) - } + @Nested + inner class `braces always` { + val subject = createSubject("always", "always") @Test - fun `reports else with braces but if without`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) - println() - else { - println() - } - } - """.trimIndent() - ) + fun `does not report correct block with braces`() { + val findings = subject.compileAndLint(BLOCK_WITH_BRACES_PASS) - assertThat(findings).hasSize(1) - assertThat(findings).hasTextLocations(14 to 16) - } - - @Test - fun `reports both then and else 1`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) println() - else println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(14 to 16, 38 to 42) + assertThat(findings).isEmpty() } @Test - fun `reports both then and else 2`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) println() else - println() - } - """.trimIndent() + fun `reports incorrect block with braces`() { + val findings = subject.compileAndLint(BLOCK_WITH_BRACES_FAIL) + + assertThat(findings).hasSize(10) + assertThat(findings).hasTextLocations( + 109 to 111, + 201 to 205, + 254 to 256, + 328 to 330, + 301 to 303, + 469 to 473, + 576 to 580, + 608 to 610, + 770 to 774, + 867 to 869 ) - - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(14 to 16, 34 to 38) } } @Nested - inner class `multiline if statements with braces` { - val subject = createSubject("never", "always") + inner class `braces never` { + val subject = createSubject("never", "never") @Test - fun `does not report if statements with braces`() { - val code = """ - fun f() { - if (true) { - println() - } - if (true) - { - println() - } - if (true) - { println() } - } - """.trimIndent() - assertThat(subject.compileAndLint(code)).isEmpty() - } - } + fun `does not report correct block without braces`() { + val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_PASS) - @Nested - inner class `single-line if statements which don't need braces` { - val subject = createSubject("never", "always") - - @Test - fun `does not report single-line if statements`() { - val code = """ - fun f() { - if (true) println() - if (true) println() else println() - if (true) println() else if (false) println() else println() - } - """.trimIndent() - assertThat(subject.compileAndLint(code)).isEmpty() + assertThat(findings).isEmpty() } - } - - @Nested - inner class `multi-line when following an else statement without requiring braces` { - val subject = createSubject("never", "always") @Test - fun `does not report multi-line when`() { - val code = """ - fun f(i: Int) { - if (true) { - println() - } else when(i) { - 1 -> println(1) - else -> println() - } - } - """.trimIndent() - assertThat(subject.compileAndLint(code)).isEmpty() + fun `reports incorrect block without braces`() { + val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_FAIL) + + assertThat(findings).hasSize(8) + assertThat(findings).hasTextLocations( + 95 to 97, + 185 to 189, + 278 to 282, + 365 to 367, + 479 to 483, + 515 to 517, + 655 to 657, + 815 to 819 + ) } } } From d04ac5118cc826c55304e61320d150132577ddf0 Mon Sep 17 00:00:00 2001 From: "Vitaly V. Pinchuk" Date: Thu, 19 Jan 2023 21:52:40 -0400 Subject: [PATCH 04/33] Rework --- .../src/main/resources/deprecation.properties | 2 +- .../rules/style/BracesOnIfStatements.kt | 150 +++++--- .../detekt/rules/style/StyleGuideProvider.kt | 4 +- .../optional/MandatoryBracesIfStatements.kt | 5 +- .../rules/style/BracesOnIfStatementsSpec.kt | 323 ++++++++++++------ .../MandatoryBracesIfStatementsSpec.kt | 1 + 6 files changed, 318 insertions(+), 167 deletions(-) diff --git a/detekt-core/src/main/resources/deprecation.properties b/detekt-core/src/main/resources/deprecation.properties index 5bfaf9b2a38..b4c0163db38 100644 --- a/detekt-core/src/main/resources/deprecation.properties +++ b/detekt-core/src/main/resources/deprecation.properties @@ -8,7 +8,7 @@ potential-bugs>RedundantElseInWhen=Rule deprecated as compiler performs this che naming>FunctionParameterNaming>ignoreOverriddenFunctions=Use `ignoreOverridden` instead naming>MemberNameEqualsClassName>ignoreOverriddenFunction=Use `ignoreOverridden` instead style>FunctionOnlyReturningConstant>excludeAnnotatedFunction=Use `ignoreAnnotated` instead -style>MandatoryBracesIfStatements=Use 'BracesOnIfStatements()' instead +style>MandatoryBracesIfStatements=Use 'BracesOnIfStatements' with 'always' configuration instead style>UnderscoresInNumericLiterals>acceptableDecimalLength=Use `acceptableLength` instead style>UnnecessaryAbstractClass>excludeAnnotatedClasses=Use `ignoreAnnotated` instead style>UseDataClass>excludeAnnotatedClasses=Use `ignoreAnnotated` instead diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt index 6d41b4ec4ee..a017a3716ed 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt @@ -17,57 +17,105 @@ import org.jetbrains.kotlin.psi.KtIfExpression * This rule detects `if` statements which do not comply with the specified rules. * Keeping braces consistent would improve readability and avoid possible errors. * - * SingleLine if-statement has no '\n': + * The available options are: + * * `always`: forces braces on all `if` and `else` branches in the whole codebase. + * * `consistent`: ensures that braces are consistent within each `if`-`else if`-`else` chain. + * If there's a brace on one of the branches, all branches should have it. + * * `necessary`: forces no braces on any `if` and `else` branches in the whole codebase + * except where necessary for multi-statement branches. + * * `never`: forces no braces on any `if` and `else` branches in the whole codebase. + * + * SingleLine if-statement has no \n: + * ``` * if (a) b else c - * MultiLine if-statement has at least one '\n': + * ``` + * MultiLine if-statement has at least one \n: + * ``` * if (a) b * else c + * ``` * * + * * // singleLine = 'never' * if (a) { b } else { c } + * * if (a) { b } else c + * + * if (a) b else { c; d } + * * // multiLine = 'never' - * if (a) { b } - * else { c } + * if (a) { + * b + * } else { + * c + * } * * // singleLine = 'always' * if (a) b else c + * * if (a) { b } else c + * * // multiLine = 'always' - * if (a) { b } - * else c + * if (a) { + * b + * } else + * c * * // singleLine = 'consistent' * if (a) b else { c } + * if (a) b else if (c) d else { e } + * * // multiLine = 'consistent' - * if (a) b - * else { c } + * if (a) + * b + * else { + * c + * } * * * * // singleLine = 'never' * if (a) b else c - * if (a) b else { c; d; } // multi-expression + * * // multiLine = 'never' - * if (a) b - * else c + * if (a) + * b + * else + * c * * // singleLine = 'always' * if (a) { b } else { c } + * * if (a) { b } else if (c) { d } + * * // multiLine = 'always' - * if (a) { b } - * else { c } - * if (a) { b } - * else if (c) { d } + * if (a) { + * b + * } else { + * c + * } + * + * if (a) { + * b + * } else if (c) { + * d + * } * * // singleLine = 'consistent' * if (a) { b } else { c } - * if (a) b else { c; d } // multi-expression + * + * if (a) b else c + * + * if (a) { b } else { c; d } + * * // multiLine = 'consistent' - * if (a) { b } - * else { c } + * if (a) { + * b + * } else { + * c + * } + * * if (a) b * else c * @@ -82,10 +130,10 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { ) @Configuration("single-line braces policy") - private val singleLine: BracePolicy by config(BRACES_NEVER) { BracePolicy.getValue(it) } + private val singleLine: BracePolicy by config("never") { BracePolicy.getValue(it) } @Configuration("multi-line braces policy") - private val multiLine: BracePolicy by config(BRACES_ALWAYS) { BracePolicy.getValue(it) } + private val multiLine: BracePolicy by config("always") { BracePolicy.getValue(it) } override fun visitIfExpression(expression: KtIfExpression) { super.visitIfExpression(expression) @@ -110,38 +158,45 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { private fun validate(list: List, policy: BracePolicy) { when (policy) { BracePolicy.Always -> { - if (!list.all { hasBraces(it) }) { - val violator = list.first { !hasBraces(it) } - report(violator, policy) - } + list.filter { !hasBraces(it) }.report(policy) + } + + BracePolicy.Necessary -> { + list.filter { !isMultiStatement(it) && hasBraces(it) }.report(policy) } BracePolicy.Never -> { - if (!list.all { !hasBraces(it) }) { - val violator = list.first { hasBraces(it) } - report(violator, policy) - } + list.filter { hasBraces(it) }.report(policy) } BracePolicy.Consistent -> { - val reference = hasBraces(list.first()) - if (!list.all { hasBraces(it) == reference }) { - val violator = list.first { hasBraces(it) != reference } - report(violator, policy) + val braces = list.count { hasBraces(it) } + val noBraces = list.count { !hasBraces(it) } + if (braces != 0 && noBraces != 0) { + list.take(1).report(policy) } } } } + private fun List.report(policy: BracePolicy) { + this.forEach { report(it, policy) } + } + private fun report(violator: KtExpression, policy: BracePolicy) { val iff = violator.parent.parent as KtIfExpression val reported = when { iff.then === violator -> iff.ifKeyword - iff.`else` === violator && isMultiStatement(violator) -> iff.ifKeyword iff.`else` === violator -> iff.elseKeyword - else -> error("Violating element ($violator) is not part of this if: $iff") + else -> error("Violating element (${violator.text}) is not part of this if (${iff.text})") + } + 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." + BracePolicy.Necessary -> "Extra braces exist on this branch, remove them (ignore multi-statement)." + BracePolicy.Never -> "Extra braces exist on this branch, remove them." } - report(CodeSmell(issue, Entity.from(reported ?: violator), issue.description + ": $policy")) + report(CodeSmell(issue, Entity.from(reported ?: violator), message)) } private fun isMultiStatement(expression: KtExpression) = @@ -152,27 +207,16 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { private fun hasBraces(expression: KtExpression): Boolean = expression is KtBlockExpression - companion object { - private const val BRACES_ALWAYS = "always" - private const val BRACES_NEVER = "never" - private const val BRACES_CONSISTENT = "consistent" - } - - enum class BracePolicy { - Always, - Never, - Consistent; + enum class BracePolicy(val config: String) { + Always("always"), + Consistent("consistent"), + Necessary("necessary"), + Never("never"); companion object { fun getValue(arg: String): BracePolicy = - when (arg) { - BRACES_NEVER -> Never - BRACES_ALWAYS -> Always - BRACES_CONSISTENT -> Consistent - else -> error( - "Unknown value $arg, allowed values are: $BRACES_NEVER|$BRACES_ALWAYS|$BRACES_CONSISTENT" - ) - } + values().singleOrNull { it.config == arg } + ?: error("Unknown value $arg, allowed values are: ${values().joinToString("|")}") } } } diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt index 18aaf120aa1..cf5cb3d2b80 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt @@ -4,7 +4,6 @@ import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.RuleSet import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.DefaultRuleSetProvider -import io.gitlab.arturbosch.detekt.rules.style.optional.MandatoryBracesIfStatements import io.gitlab.arturbosch.detekt.rules.style.optional.MandatoryBracesLoops import io.gitlab.arturbosch.detekt.rules.style.optional.OptionalUnit import io.gitlab.arturbosch.detekt.rules.style.optional.PreferToOverPairSyntax @@ -76,7 +75,8 @@ class StyleGuideProvider : DefaultRuleSetProvider { UnnecessaryLet(config), MayBeConst(config), PreferToOverPairSyntax(config), - MandatoryBracesIfStatements(config), + @Suppress("DEPRECATION") + io.gitlab.arturbosch.detekt.rules.style.optional.MandatoryBracesIfStatements(config), BracesOnIfStatements(config), MandatoryBracesLoops(config), NullableBooleanCheck(config), diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt index dfaa22d9309..d6a03c51c48 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt @@ -28,10 +28,7 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings * val x = if (condition) 5 else 4 * */ -@Deprecated( - "Use 'BracesOnIfStatements()' instead", - ReplaceWith("BracesOnIfStatements(config)") -) +@Deprecated("Use 'BracesOnIfStatements' with 'always' configuration instead") class MandatoryBracesIfStatements(config: Config = Config.empty) : Rule(config) { override val issue = Issue( diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index b7867bd59ae..b7d60a66988 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -8,47 +8,57 @@ import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test @Language("kotlin") -private const val BLOCK_WITHOUT_BRACES_PASS = """ +private val BLOCK_WITHOUT_BRACES_PASS = """ fun f() { - if (true) println() + if (true) println() + if (true) - println() - if (true) println() else println() + println() + + if (true) println() else println() + if (true) println() else println() if (true) println() else if (true) println() else println() + if (true) println() - else - if (true) println() else println() + else if (true) + println() + else + println() - val a = 0 - when (a) { - 0 -> if (true) println() else println() - 1 -> if (true) + when (true) { + true -> if (true) println() else println() + true -> if (true) println() else println() - 2 -> if (true) println() else if (true) println() else println() - 3 -> if (true) + true -> if (true) println() else if (true) println() else println() + else -> if (true) println() - else - if (true) println() else println() + else if (true) + println() + else + println() } } -""" +""".trimIndent() @Language("kotlin") -private const val BLOCK_WITH_BRACES_PASS = """ +private val BLOCK_WITH_BRACES_PASS = """ fun f() { - if (true) { println() } + if (true) { println() } + if (true) { println() } - if (true) { println() } else { println() } + + if (true) { println() } else { println() } + if (true) { println() } else { @@ -56,7 +66,9 @@ private const val BLOCK_WITH_BRACES_PASS = """ } if (true) { println() } else if (true) { println() } else { println() } + if (true) { println() } else { if (true) { println() } else { println() } } + if (true) { println() } else if (true) { @@ -65,16 +77,15 @@ private const val BLOCK_WITH_BRACES_PASS = """ println() } - val a = 0 - when (a) { - 0 -> if (true) { println() } else { println() } - 1 -> if (true) { + when (true) { + true -> if (true) { println() } else { println() } + true -> if (true) { println() } else { println() } - 2 -> if (true) { println() } else if (true) { println() } else { println() } - 3 -> if (true) { + true -> if (true) { println() } else if (true) { println() } else { println() } + else -> if (true) { println() } else if (true) { println() @@ -83,15 +94,13 @@ private const val BLOCK_WITH_BRACES_PASS = """ } } } -""" +""".trimIndent() @Language("kotlin") -private const val BLOCK_WITHOUT_BRACES_FAIL = """ +private val BLOCK_WITHOUT_BRACES_FAIL = """ fun f() { - if (true) println() - if (true) - println() - if (true) { println() } else println() + if (true) { println() } else println() + if (true) println() else { @@ -99,41 +108,46 @@ private const val BLOCK_WITHOUT_BRACES_FAIL = """ } if (true) println() else if (true) println() else { println() } + if (true) println() - else - if (true) { println() } else println() - - val a = 0 - when (a) { - 0 -> if (true) println() else { println() } - 1 -> if (true) - { println() } - else + else if (true) { + println() + } else + println() + + when (true) { + true -> if (true) println() else { println() } + true -> if (true) { + println() + } else println() - 2 -> if (true) println() else if (true) { println() } else println() - 3 -> if (true) + true -> if (true) println() else if (true) { println() } else println() + else -> if (true) println() - else - if (true) println() else { println() } + else if (true) + println() + else { + println() + } } } -""" +""".trimIndent() @Language("kotlin") -private const val BLOCK_WITH_BRACES_FAIL = """ +private val BLOCK_WITH_BRACES_FAIL = """ fun f() { - if (true) { println() } + if (true) println() else { println() } + if (true) { println() - } - if (true) println() else { println() } - if (true) { + } else println() - } else println() if (true) { println() } else if (true) println() else { println() } + if (true) println() else { if (true) println() else println() } + if (true) { println() } else if (true) { @@ -141,15 +155,14 @@ private const val BLOCK_WITH_BRACES_FAIL = """ } else println() - val a = 0 - when (a) { - 0 -> if (true) { println() } else println() - 1 -> if (true) println() + when (true) { + true -> if (true) { println() } else println() + true -> if (true) println() else { println() } - 2 -> if (true) { println() } else if (true) { println() } else println() - 3 -> if (true) { + true -> if (true) { println() } else if (true) { println() } else println() + else -> if (true) { println() } else if (true) println() @@ -158,7 +171,38 @@ private const val BLOCK_WITH_BRACES_FAIL = """ } } } -""" +""".trimIndent() + +@Language("kotlin") +private val BLOCK_NECESSARY_BRACES_PASS = """ + fun f() { + if (true) println() else if (true) { println(); println() } else println() + + if (true) + println() + else if (true) { + println() + println() + } else + println() + } +""".trimIndent() + +@Language("kotlin") +private val BLOCK_NECESSARY_BRACES_FAIL = """ + fun f() { + if (true) println() else if (true) { println(); println() } else { println() } + + if (true) + println() + else if (true) { + println() + println() + } else { + println() + } + } +""".trimIndent() class BracesOnIfStatementsSpec { @@ -172,6 +216,37 @@ class BracesOnIfStatementsSpec { return BracesOnIfStatements(config) } + @Nested + inner class `braces always` { + val subject = createSubject("always", "always") + + @Test + fun `does not report correct block with braces`() { + val findings = subject.compileAndLint(BLOCK_WITH_BRACES_PASS) + + assertThat(findings).isEmpty() + } + + @Test + fun `reports incorrect block with braces`() { + val findings = subject.compileAndLint(BLOCK_WITH_BRACES_FAIL) + + assertThat(findings).hasTextLocations( + 15 to 17, + 95 to 99, + 152 to 154, + 223 to 225, + 243 to 247, + 196 to 198, + 345 to 349, + 428 to 432, + 459 to 461, + 608 to 612, + 696 to 698 + ) + } + } + @Nested inner class `braces consistent` { val subject = createSubject("consistent", "consistent") @@ -194,16 +269,15 @@ class BracesOnIfStatementsSpec { fun `reports inconsistent block without braces`() { val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_FAIL) - assertThat(findings).hasSize(8) assertThat(findings).hasTextLocations( - 119 to 123, - 185 to 189, - 278 to 282, - 365 to 367, - 479 to 483, - 577 to 581, - 655 to 657, - 815 to 819 + 15 to 17, + 59 to 61, + 129 to 131, + 198 to 200, + 331 to 333, + 386 to 388, + 487 to 489, + 567 to 569 ) } @@ -211,17 +285,16 @@ class BracesOnIfStatementsSpec { fun `reports inconsistent block with braces`() { val findings = subject.compileAndLint(BLOCK_WITH_BRACES_FAIL) - assertThat(findings).hasSize(9) assertThat(findings).hasTextLocations( - 129 to 133, - 201 to 205, - 254 to 256, - 321 to 325, - 469 to 473, - 576 to 580, - 644 to 648, - 770 to 774, - 867 to 869 + 15 to 17, + 59 to 61, + 123 to 125, + 196 to 198, + 265 to 267, + 404 to 406, + 459 to 461, + 555 to 557, + 639 to 641 ) } @@ -234,67 +307,103 @@ class BracesOnIfStatementsSpec { } """.trimIndent() ) - assertThat(findings).hasSize(1) assertThat(findings).hasTextLocations(14 to 16) } } @Nested - inner class `braces always` { - val subject = createSubject("always", "always") + inner class `braces never` { + val subject = createSubject("never", "never") @Test - fun `does not report correct block with braces`() { - val findings = subject.compileAndLint(BLOCK_WITH_BRACES_PASS) + fun `does not report correct block without braces`() { + val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_PASS) assertThat(findings).isEmpty() } @Test - fun `reports incorrect block with braces`() { - val findings = subject.compileAndLint(BLOCK_WITH_BRACES_FAIL) + fun `reports incorrect block without braces`() { + val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_FAIL) - assertThat(findings).hasSize(10) assertThat(findings).hasTextLocations( - 109 to 111, - 201 to 205, - 254 to 256, - 328 to 330, - 301 to 303, - 469 to 473, - 576 to 580, - 608 to 610, - 770 to 774, - 867 to 869 + 15 to 17, + 93 to 97, + 174 to 178, + 237 to 239, + 351 to 355, + 386 to 388, + 512 to 514, + 670 to 674 ) } } @Nested - inner class `braces never` { - val subject = createSubject("never", "never") + inner class `braces necessary` { + val subject = createSubject("necessary", "necessary") @Test fun `does not report correct block without braces`() { - val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_PASS) + val findings = subject.compileAndLint(BLOCK_NECESSARY_BRACES_PASS) assertThat(findings).isEmpty() } @Test fun `reports incorrect block without braces`() { - val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_FAIL) + val findings = subject.compileAndLint(BLOCK_NECESSARY_BRACES_FAIL) + + assertThat(findings).hasTextLocations( + 75 to 79, + 193 to 197 + ) + } + } + + @Nested + inner class `mixed policy` { + val subject = createSubject("never", "always") + + @Test + fun `does not report correct block`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) println() else println() + + if (true) { + println() + } else { + println() + } + } + + """.trimIndent() + ) + + assertThat(findings).isEmpty() + } + + @Test + fun `reports incorrect block`() { + val findings = subject.compileAndLint( + """ + fun f() { + if (true) println() else { println() } + + if (true) { + println() + } else + println() + } + + """.trimIndent() + ) - assertThat(findings).hasSize(8) assertThat(findings).hasTextLocations( - 95 to 97, - 185 to 189, - 278 to 282, - 365 to 367, - 479 to 483, - 515 to 517, - 655 to 657, - 815 to 819 + 34 to 38, + 95 to 99 ) } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt index 8b47f72a371..5b325bb2f17 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test class MandatoryBracesIfStatementsSpec { + @Suppress("DEPRECATION") val subject = MandatoryBracesIfStatements(Config.empty) @Nested From 5075b39d17651f773766bb38a9fbe20e07a7093c Mon Sep 17 00:00:00 2001 From: "Vitaly V. Pinchuk" Date: Mon, 23 Jan 2023 18:45:17 -0400 Subject: [PATCH 05/33] Add braces check on if statements Introduce the rule that checks for braces on if statements using specified policy (never|necessary|consistent|always). --- detekt-core/src/main/resources/default-detekt-config.yml | 2 ++ detekt-core/src/main/resources/deprecation.properties | 1 - .../arturbosch/detekt/rules/style/BracesOnIfStatements.kt | 4 ++-- .../arturbosch/detekt/rules/style/StyleGuideProvider.kt | 4 ++-- .../rules/style/optional/MandatoryBracesIfStatements.kt | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/detekt-core/src/main/resources/default-detekt-config.yml b/detekt-core/src/main/resources/default-detekt-config.yml index 559029aa147..edd6a69fade 100644 --- a/detekt-core/src/main/resources/default-detekt-config.yml +++ b/detekt-core/src/main/resources/default-detekt-config.yml @@ -602,6 +602,8 @@ style: ignoreEnums: false ignoreRanges: false ignoreExtensionFunctions: true + MandatoryBracesIfStatements: + active: false MandatoryBracesLoops: active: false MaxChainedCallsOnSameLine: diff --git a/detekt-core/src/main/resources/deprecation.properties b/detekt-core/src/main/resources/deprecation.properties index b4c0163db38..5175f2ca810 100644 --- a/detekt-core/src/main/resources/deprecation.properties +++ b/detekt-core/src/main/resources/deprecation.properties @@ -8,7 +8,6 @@ potential-bugs>RedundantElseInWhen=Rule deprecated as compiler performs this che naming>FunctionParameterNaming>ignoreOverriddenFunctions=Use `ignoreOverridden` instead naming>MemberNameEqualsClassName>ignoreOverriddenFunction=Use `ignoreOverridden` instead style>FunctionOnlyReturningConstant>excludeAnnotatedFunction=Use `ignoreAnnotated` instead -style>MandatoryBracesIfStatements=Use 'BracesOnIfStatements' with 'always' configuration instead style>UnderscoresInNumericLiterals>acceptableDecimalLength=Use `acceptableLength` instead style>UnnecessaryAbstractClass>excludeAnnotatedClasses=Use `ignoreAnnotated` instead style>UseDataClass>excludeAnnotatedClasses=Use `ignoreAnnotated` instead diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt index a017a3716ed..627238a381a 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt @@ -25,11 +25,11 @@ import org.jetbrains.kotlin.psi.KtIfExpression * except where necessary for multi-statement branches. * * `never`: forces no braces on any `if` and `else` branches in the whole codebase. * - * SingleLine if-statement has no \n: + * SingleLine if-statement has no line break (\n): * ``` * if (a) b else c * ``` - * MultiLine if-statement has at least one \n: + * MultiLine if-statement has at least one line break (\n): * ``` * if (a) b * else c diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt index cf5cb3d2b80..18aaf120aa1 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt @@ -4,6 +4,7 @@ import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.RuleSet import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.DefaultRuleSetProvider +import io.gitlab.arturbosch.detekt.rules.style.optional.MandatoryBracesIfStatements import io.gitlab.arturbosch.detekt.rules.style.optional.MandatoryBracesLoops import io.gitlab.arturbosch.detekt.rules.style.optional.OptionalUnit import io.gitlab.arturbosch.detekt.rules.style.optional.PreferToOverPairSyntax @@ -75,8 +76,7 @@ class StyleGuideProvider : DefaultRuleSetProvider { UnnecessaryLet(config), MayBeConst(config), PreferToOverPairSyntax(config), - @Suppress("DEPRECATION") - io.gitlab.arturbosch.detekt.rules.style.optional.MandatoryBracesIfStatements(config), + MandatoryBracesIfStatements(config), BracesOnIfStatements(config), MandatoryBracesLoops(config), NullableBooleanCheck(config), diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt index d6a03c51c48..4ed326f97cc 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings * val x = if (condition) 5 else 4 * */ -@Deprecated("Use 'BracesOnIfStatements' with 'always' configuration instead") +// @Deprecated("Use `BracesOnIfStatements` with `always` configuration instead") class MandatoryBracesIfStatements(config: Config = Config.empty) : Rule(config) { override val issue = Issue( From 747b15808939920858a8d94e81b2dd6abd473123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Wed, 25 Jan 2023 16:32:18 +0000 Subject: [PATCH 06/33] Start rewriting tests --- .../rules/style/BracesOnIfStatementsSpec.kt | 565 +++++++----------- 1 file changed, 220 insertions(+), 345 deletions(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index b7d60a66988..60e550eba18 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -1,410 +1,285 @@ +@file:Suppress("ClassName", "PrivatePropertyName") + package io.gitlab.arturbosch.detekt.rules.style +import io.gitlab.arturbosch.detekt.api.Finding import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint -import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.DynamicNode +import org.junit.jupiter.api.DynamicTest.dynamicTest import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestFactory + +private val NO_BRACES_SINGLE: String = """ + if (true) println() + if (true) println() else println() + if (true) println() else if println() + if (true) println() else if println() else println() + if (true) println() else if println() else if println() else println() +""".trimIndent() -@Language("kotlin") -private val BLOCK_WITHOUT_BRACES_PASS = """ - fun f() { - if (true) println() +private val NO_BRACES_SINGLE_LOCATIONS: Array> = arrayOf( + 14 to 16, + 38 to 40, + 58 to 62, + 77 to 79, + 102 to 104, + 119 to 121, + 144 to 146, + 157 to 161, + 176 to 178, + 201 to 203, + 219 to 221, + 232 to 236, +) + +private val ALL_BRACES_SINGLE: String = """ + if (true) { println() } + if (true) { println() } else { println() } + if (true) { println() } else if { println() } + if (true) { println() } else if { println() } else { println() } + if (true) { println() } else if { println() } else if { println() } else { println() } +""".trimIndent() - if (true) - println() +private val ALL_BRACES_SINGLE_LOCATIONS: Array> = arrayOf( + 14 to 16, + 42 to 44, + 66 to 70, + 89 to 91, + 118 to 120, + 139 to 141, + 168 to 170, + 185 to 189, + 208 to 210, + 237 to 239, + 259 to 261, + 276 to 280, +) + +private val IF_BRACES_SINGLE: String = """ + if (true) { println() } + if (true) { println() } else println() + if (true) { println() } else if println() + if (true) { println() } else if println() else println() + if (true) { println() } else if println() else if println() else println() +""".trimIndent() - if (true) println() else println() +private val IF_BRACES_SINGLE_BRACE_LOCATIONS: Array> = arrayOf( + 14 to 16, + 42 to 44, + 85 to 87, + 131 to 133, + 192 to 194, +) + +private val IF_BRACES_SINGLE_NO_BRACE_LOCATIONS: Array> = arrayOf( + 66 to 70, + 114 to 116, + 160 to 162, + 173 to 177, + 221 to 223, + 239 to 241, + 252 to 256, +) + +private val ELSE_BRACES_SINGLE: String = """ + if (true) println() + if (true) println() else { println() } + if (true) println() else if println() + if (true) println() else if println() else { println() } + if (true) println() else if println() else if println() + if (true) println() else if println() else if println() else { println() } +""".trimIndent() - if (true) - println() - else - println() +private const val NOT_RELEVANT = "*" - if (true) println() else if (true) println() else println() +class BracesOnIfStatementsSpec { - if (true) - println() - else if (true) - println() - else - println() - - when (true) { - true -> if (true) println() else println() - true -> if (true) - println() - else - println() - true -> if (true) println() else if (true) println() else println() - else -> if (true) - println() - else if (true) - println() - else - println() - } + private fun createSubject(singleLine: String, multiLine: String): BracesOnIfStatements { + val config = TestConfig( + mapOf( + "singleLine" to singleLine, + "multiLine" to multiLine + ) + ) + return BracesOnIfStatements(config) } -""".trimIndent() -@Language("kotlin") -private val BLOCK_WITH_BRACES_PASS = """ - fun f() { - if (true) { println() } + private fun BracesOnIfStatements.compileAndLintInF(code: String): List = + compileAndLint( + """ + fun f() { + ${code.prependIndent(" ").trimStart()} + } + """.trimIndent() + ) - if (true) { - println() - } + @Nested + inner class singleLine { - if (true) { println() } else { println() } + @Nested + inner class `=always` { - if (true) { - println() - } else { - println() - } + private val singleLine = BracesOnIfStatements.BracePolicy.Always.config - if (true) { println() } else if (true) { println() } else { println() } + @TestFactory + fun `missing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { + val findings = compileAndLintInF(NO_BRACES_SINGLE) - if (true) { println() } else { if (true) { println() } else { println() } } + assertThat(findings).hasTextLocations(*NO_BRACES_SINGLE_LOCATIONS) + } - if (true) { - println() - } else if (true) { - println() - } else { - println() - } + @TestFactory + fun `existing braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { + val findings = compileAndLintInF(ALL_BRACES_SINGLE) - when (true) { - true -> if (true) { println() } else { println() } - true -> if (true) { - println() - } else { - println() - } - true -> if (true) { println() } else if (true) { println() } else { println() } - else -> if (true) { - println() - } else if (true) { - println() - } else { - println() - } - } - } -""".trimIndent() + assertThat(findings).isEmpty() + } -@Language("kotlin") -private val BLOCK_WITHOUT_BRACES_FAIL = """ - fun f() { - if (true) { println() } else println() + @TestFactory + fun `partially missing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { + val findings = compileAndLintInF(IF_BRACES_SINGLE) - if (true) - println() - else { - println() + assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_NO_BRACE_LOCATIONS) + } } - if (true) println() else if (true) println() else { println() } - - if (true) - println() - else if (true) { - println() - } else - println() - - when (true) { - true -> if (true) println() else { println() } - true -> if (true) { - println() - } else - println() - true -> if (true) println() else if (true) { println() } else println() - else -> if (true) - println() - else if (true) - println() - else { - println() - } - } - } -""".trimIndent() + @Nested + inner class `=never` { -@Language("kotlin") -private val BLOCK_WITH_BRACES_FAIL = """ - fun f() { - if (true) println() else { println() } + private val singleLine = BracesOnIfStatements.BracePolicy.Never.config - if (true) { - println() - } else - println() + @TestFactory + fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { + val findings = compileAndLintInF(NO_BRACES_SINGLE) - if (true) { println() } else if (true) println() else { println() } + assertThat(findings).isEmpty() + } - if (true) println() else { if (true) println() else println() } + @TestFactory + fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { + val findings = compileAndLintInF(ALL_BRACES_SINGLE) - if (true) { - println() - } else if (true) { - println() - } else - println() + assertThat(findings).hasTextLocations(*ALL_BRACES_SINGLE_LOCATIONS) + } - when (true) { - true -> if (true) { println() } else println() - true -> if (true) println() - else { - println() - } - true -> if (true) { println() } else if (true) { println() } else println() - else -> if (true) { - println() - } else if (true) - println() - else { - println() - } - } - } -""".trimIndent() - -@Language("kotlin") -private val BLOCK_NECESSARY_BRACES_PASS = """ - fun f() { - if (true) println() else if (true) { println(); println() } else println() - - if (true) - println() - else if (true) { - println() - println() - } else - println() - } -""".trimIndent() + @TestFactory + fun `partially extra braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { + val findings = compileAndLintInF(IF_BRACES_SINGLE) -@Language("kotlin") -private val BLOCK_NECESSARY_BRACES_FAIL = """ - fun f() { - if (true) println() else if (true) { println(); println() } else { println() } - - if (true) - println() - else if (true) { - println() - println() - } else { - println() + assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_BRACE_LOCATIONS) + } } - } -""".trimIndent() -class BracesOnIfStatementsSpec { + @Nested + inner class `=necessary` { - private fun createSubject(singleLine: String, multiLine: String): BracesOnIfStatements { - val config = TestConfig( - mapOf( - "singleLine" to singleLine, - "multiLine" to multiLine - ) - ) - return BracesOnIfStatements(config) - } + private val singleLine = BracesOnIfStatements.BracePolicy.Necessary.config - @Nested - inner class `braces always` { - val subject = createSubject("always", "always") + @TestFactory + fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { + val findings = compileAndLintInF(NO_BRACES_SINGLE) - @Test - fun `does not report correct block with braces`() { - val findings = subject.compileAndLint(BLOCK_WITH_BRACES_PASS) + assertThat(findings).isEmpty() + } - assertThat(findings).isEmpty() - } + @TestFactory + fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { + val findings = compileAndLintInF(ALL_BRACES_SINGLE) - @Test - fun `reports incorrect block with braces`() { - val findings = subject.compileAndLint(BLOCK_WITH_BRACES_FAIL) - - assertThat(findings).hasTextLocations( - 15 to 17, - 95 to 99, - 152 to 154, - 223 to 225, - 243 to 247, - 196 to 198, - 345 to 349, - 428 to 432, - 459 to 461, - 608 to 612, - 696 to 698 - ) - } - } + assertThat(findings).hasTextLocations(*ALL_BRACES_SINGLE_LOCATIONS) + } - @Nested - inner class `braces consistent` { - val subject = createSubject("consistent", "consistent") + @TestFactory + fun `partially extra braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { + val findings = compileAndLintInF(IF_BRACES_SINGLE) - @Test - fun `does not report consistent block without braces`() { - val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_PASS) - - assertThat(findings).isEmpty() + assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_BRACE_LOCATIONS) + } } - @Test - fun `does not report consistent block with braces`() { - val findings = subject.compileAndLint(BLOCK_WITH_BRACES_PASS) + @Nested + inner class `=consistent` { - assertThat(findings).isEmpty() - } + private val singleLine = BracesOnIfStatements.BracePolicy.Consistent.config - @Test - fun `reports inconsistent block without braces`() { - val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_FAIL) - - assertThat(findings).hasTextLocations( - 15 to 17, - 59 to 61, - 129 to 131, - 198 to 200, - 331 to 333, - 386 to 388, - 487 to 489, - 567 to 569 - ) - } + @TestFactory + fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { + val findings = compileAndLintInF(NO_BRACES_SINGLE) - @Test - fun `reports inconsistent block with braces`() { - val findings = subject.compileAndLint(BLOCK_WITH_BRACES_FAIL) - - assertThat(findings).hasTextLocations( - 15 to 17, - 59 to 61, - 123 to 125, - 196 to 198, - 265 to 267, - 404 to 406, - 459 to 461, - 555 to 557, - 639 to 641 - ) - } - - @Test - fun `reports if when else is multi-statement`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) println() else { println(); println() } - } - """.trimIndent() - ) - assertThat(findings).hasTextLocations(14 to 16) - } - } + assertThat(findings).isEmpty() + } - @Nested - inner class `braces never` { - val subject = createSubject("never", "never") + @TestFactory + fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { + val findings = compileAndLintInF(ALL_BRACES_SINGLE) - @Test - fun `does not report correct block without braces`() { - val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_PASS) + assertThat(findings).isEmpty() + } - assertThat(findings).isEmpty() - } + @TestFactory + fun `partial braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { + val findings = compileAndLintInF(IF_BRACES_SINGLE) - @Test - fun `reports incorrect block without braces`() { - val findings = subject.compileAndLint(BLOCK_WITHOUT_BRACES_FAIL) - - assertThat(findings).hasTextLocations( - 15 to 17, - 93 to 97, - 174 to 178, - 237 to 239, - 351 to 355, - 386 to 388, - 512 to 514, - 670 to 674 - ) + assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_BRACE_LOCATIONS.drop(1).toTypedArray()) + } } } - @Nested - inner class `braces necessary` { - val subject = createSubject("necessary", "necessary") - - @Test - fun `does not report correct block without braces`() { - val findings = subject.compileAndLint(BLOCK_NECESSARY_BRACES_PASS) - - assertThat(findings).isEmpty() - } - - @Test - fun `reports incorrect block without braces`() { - val findings = subject.compileAndLint(BLOCK_NECESSARY_BRACES_FAIL) + @TestFactory + fun `whens are not flagged`() = braceTests(NOT_RELEVANT, NOT_RELEVANT) { + val findings = compileAndLintInF( + """ + when (true) { + true -> println() + else -> println() + } + """.trimIndent() + ) - assertThat(findings).hasTextLocations( - 75 to 79, - 193 to 197 - ) - } + assertThat(findings).isEmpty() } - @Nested - inner class `mixed policy` { - val subject = createSubject("never", "always") - - @Test - fun `does not report correct block`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) println() else println() - - if (true) { - println() - } else { - println() + private fun braceTests( + singleLine: String, + multiLine: String, + test: BracesOnIfStatements.() -> Unit + ): List { + val singleOptions = options(singleLine) + val multiOptions = options(multiLine) + return when { + singleOptions.size > 1 && multiOptions.size > 1 -> + singleOptions.flatMap { singleLineOption -> + multiOptions.map { multiLineOption -> + dynamicTest("singleLine=${singleLineOption}, multiLine=${multiLineOption}") { + createSubject(singleLineOption, multiLineOption).test() } } - - """.trimIndent() - ) + } - assertThat(findings).isEmpty() - } + singleOptions.size > 1 && multiOptions.size == 1 -> + singleOptions.map { singleLineOption -> + dynamicTest("singleLine=${singleLineOption}}") { + createSubject(singleLineOption, multiOptions.single()).test() + } + } - @Test - fun `reports incorrect block`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) println() else { println() } - - if (true) { - println() - } else - println() + singleOptions.size == 1 && multiOptions.size > 1 -> + multiOptions.map { multiLineOption -> + dynamicTest("multiLine=${multiLineOption}") { + createSubject(singleOptions.single(), multiLineOption).test() } - - """.trimIndent() - ) + } - assertThat(findings).hasTextLocations( - 34 to 38, - 95 to 99 - ) + else -> + error("No options to test: $singleLine -> $singleOptions, $multiLine -> $multiOptions") } } + + private fun options(option: String): List = + if (option == NOT_RELEVANT) + BracesOnIfStatements.BracePolicy.values().map { it.config } + else + listOf(option) } From 57331f643a01816b4103630c2ac7c7156e958bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Wed, 25 Jan 2023 17:07:40 +0000 Subject: [PATCH 07/33] BROKEN COMPILER org.jetbrains.kotlin.backend.common.BackendException: Backend Internal error: Exception during IR lowering File being compiled: P:/projects/contrib/github-detekt/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt The root cause java.lang.RuntimeException was thrown at: org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:49) at org.jetbrains.kotlin.backend.common.CodegenUtil.reportBackendException(CodegenUtil.kt:241) at org.jetbrains.kotlin.backend.common.CodegenUtil.reportBackendException$default(CodegenUtil.kt:236) at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invokeSequential(performByIrFile.kt:68) at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invoke(performByIrFile.kt:55) at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invoke(performByIrFile.kt:41) at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.invoke(CompilerPhase.kt:96) at org.jetbrains.kotlin.backend.common.phaser.CompositePhase.invoke(PhaseBuilders.kt:29) at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.invoke(CompilerPhase.kt:96) at org.jetbrains.kotlin.backend.common.phaser.CompilerPhaseKt.invokeToplevel(CompilerPhase.kt:43) at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.invokeCodegen(JvmIrCodegenFactory.kt:312) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.runCodegen(KotlinToJVMBytecodeCompiler.kt:348) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:123) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:47) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:167) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:53) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:101) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:47) at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101) at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:485) at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:131) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:424) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:360) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.tryCompileIncrementally(IncrementalCompilerRunner.kt:194) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:89) at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:625) at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:101) at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1746) at jdk.internal.reflect.GeneratedMethodAccessor102.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:359) at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200) at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197) at java.base/java.security.AccessController.doPrivileged(Native Method) at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196) at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:562) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:796) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:677) at java.base/java.security.AccessController.doPrivileged(Native Method) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:676) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at java.base/java.lang.Thread.run(Thread.java:834) Caused by: java.lang.RuntimeException: Exception while generating code for: FUN name:partially extra braces are flagged visibility:public modality:FINAL <> ($this:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never) returnType:kotlin.collections.List annotations: TestFactory $this: VALUE_PARAMETER name: type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun partially extra braces are flagged (): kotlin.collections.List declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never' CALL 'public final fun access$braceTests ($this: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec, singleLine: kotlin.String, multiLine: kotlin.String, test: @[ExtensionFunctionType] @[ContextFunctionTypeParams(count = '1')] kotlin.Function2, kotlin.Unit>): kotlin.collections.List declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec' type=kotlin.collections.List origin=null $this: GET_FIELD 'FIELD FIELD_FOR_OUTER_THIS name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec visibility:public/*package*/ [final]' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec origin=null receiver: GET_FIELD 'FIELD FIELD_FOR_OUTER_THIS name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine visibility:public/*package*/ [final]' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine origin=null receiver: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null singleLine: CONST String type=kotlin.String value="never" multiLine: CONST String type=kotlin.String value="*" test: BLOCK type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null CLASS LAMBDA_IMPL CLASS name: modality:FINAL visibility:public/*package*/ superTypes:[kotlin.jvm.internal.Lambda; kotlin.Function2, kotlin.Unit>] $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. CONSTRUCTOR GENERATED_MEMBER_IN_CALLABLE_REFERENCE visibility:public/*package*/ <> (this$0:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never) returnType:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. [primary] VALUE_PARAMETER BOUND_RECEIVER_PARAMETER name:this$0 index:0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never BLOCK_BODY SET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never visibility:public/*package*/ [final]' type=kotlin.Unit origin=INITIALIZER_OF_FIELD_FOR_CAPTURED_VALUE receiver: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged.' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null value: GET_VAR 'this$0: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null DELEGATING_CONSTRUCTOR_CALL 'public constructor (arity: kotlin.Int) declared in kotlin.jvm.internal.Lambda' arity: CONST Int type=kotlin.Int value=2 BLOCK type=kotlin.Unit origin=null FUN name:invoke visibility:public modality:FINAL <> ($this:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged., :io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, $this$braceTests:kotlin.collections.MutableList) returnType:kotlin.Unit overridden: public abstract fun invoke (p1: P1 of kotlin.Function2, p2: P2 of kotlin.Function2): R of kotlin.Function2 [operator] declared in kotlin.Function2 $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. VALUE_PARAMETER name: index:0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements VALUE_PARAMETER name:$this$braceTests index:1 type:kotlin.collections.MutableList BLOCK_BODY CALL 'private final fun flag (: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, code: kotlin.String, vararg locations: kotlin.Pair): kotlin.Unit [inline] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never' type=kotlin.Unit origin=null $this: TYPE_OP type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=IMPLICIT_CAST typeOperand=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never visibility:public/*package*/ [final]' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null receiver: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null $receiver: GET_VAR '$this$braceTests: kotlin.collections.MutableList declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.collections.MutableList origin=null : GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements origin=null code: CONST String type=kotlin.String value="if (true) { println() }" locations: BLOCK type=kotlin.Array> origin=null VAR IR_TEMPORARY_VARIABLE name:tmp0 type:kotlin.Array> [val] CALL 'public final fun arrayOfNulls (size: kotlin.Int): kotlin.Array declared in kotlin' type=kotlin.Array> origin=null : kotlin.Pair size: CONST Int type=kotlin.Int value=1 CALL 'public final fun set (index: kotlin.Int, value: T of kotlin.Array): kotlin.Unit [operator] declared in kotlin.Array' type=kotlin.Unit origin=null $this: GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null index: CONST Int type=kotlin.Int value=0 value: CALL 'public final fun to (that: B of kotlin.TuplesKt.to): kotlin.Pair [infix] declared in kotlin.TuplesKt' type=kotlin.Pair origin=null : kotlin.Int : kotlin.Int $receiver: CONST Int type=kotlin.Int value=1 that: CONST Int type=kotlin.Int value=3 GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null CALL 'private final fun flag (: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, code: kotlin.String, vararg locations: kotlin.Pair): kotlin.Unit [inline] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never' type=kotlin.Unit origin=null $this: TYPE_OP type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=IMPLICIT_CAST typeOperand=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never visibility:public/*package*/ [final]' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null receiver: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null $receiver: GET_VAR '$this$braceTests: kotlin.collections.MutableList declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.collections.MutableList origin=null : GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements origin=null code: CONST String type=kotlin.String value="if (true) { println() } else println()" locations: BLOCK type=kotlin.Array> origin=null VAR IR_TEMPORARY_VARIABLE name:tmp0 type:kotlin.Array> [val] CALL 'public final fun arrayOfNulls (size: kotlin.Int): kotlin.Array declared in kotlin' type=kotlin.Array> origin=null : kotlin.Pair size: CONST Int type=kotlin.Int value=1 CALL 'public final fun set (index: kotlin.Int, value: T of kotlin.Array): kotlin.Unit [operator] declared in kotlin.Array' type=kotlin.Unit origin=null $this: GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null index: CONST Int type=kotlin.Int value=0 value: CALL 'public final fun to (that: B of kotlin.TuplesKt.to): kotlin.Pair [infix] declared in kotlin.TuplesKt' type=kotlin.Pair origin=null : kotlin.Int : kotlin.Int $receiver: CONST Int type=kotlin.Int value=1 that: CONST Int type=kotlin.Int value=3 GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null CALL 'private final fun flag (: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, code: kotlin.String, vararg locations: kotlin.Pair): kotlin.Unit [inline] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never' type=kotlin.Unit origin=null $this: TYPE_OP type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=IMPLICIT_CAST typeOperand=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never visibility:public/*package*/ [final]' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null receiver: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null $receiver: GET_VAR '$this$braceTests: kotlin.collections.MutableList declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.collections.MutableList origin=null : GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements origin=null code: CONST String type=kotlin.String value="if (true) { println() } else if (true) println()" locations: BLOCK type=kotlin.Array> origin=null VAR IR_TEMPORARY_VARIABLE name:tmp0 type:kotlin.Array> [val] CALL 'public final fun arrayOfNulls (size: kotlin.Int): kotlin.Array declared in kotlin' type=kotlin.Array> origin=null : kotlin.Pair size: CONST Int type=kotlin.Int value=1 CALL 'public final fun set (index: kotlin.Int, value: T of kotlin.Array): kotlin.Unit [operator] declared in kotlin.Array' type=kotlin.Unit origin=null $this: GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null index: CONST Int type=kotlin.Int value=0 value: CALL 'public final fun to (that: B of kotlin.TuplesKt.to): kotlin.Pair [infix] declared in kotlin.TuplesKt' type=kotlin.Pair origin=null : kotlin.Int : kotlin.Int $receiver: CONST Int type=kotlin.Int value=1 that: CONST Int type=kotlin.Int value=3 GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null CALL 'private final fun flag (: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, code: kotlin.String, vararg locations: kotlin.Pair): kotlin.Unit [inline] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never' type=kotlin.Unit origin=null $this: TYPE_OP type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=IMPLICIT_CAST typeOperand=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never visibility:public/*package*/ [final]' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null receiver: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null $receiver: GET_VAR '$this$braceTests: kotlin.collections.MutableList declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.collections.MutableList origin=null : GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements origin=null code: CONST String type=kotlin.String value="if (true) { println() } else if (true) println() else println()" locations: BLOCK type=kotlin.Array> origin=null VAR IR_TEMPORARY_VARIABLE name:tmp0 type:kotlin.Array> [val] CALL 'public final fun arrayOfNulls (size: kotlin.Int): kotlin.Array declared in kotlin' type=kotlin.Array> origin=null : kotlin.Pair size: CONST Int type=kotlin.Int value=1 CALL 'public final fun set (index: kotlin.Int, value: T of kotlin.Array): kotlin.Unit [operator] declared in kotlin.Array' type=kotlin.Unit origin=null $this: GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null index: CONST Int type=kotlin.Int value=0 value: CALL 'public final fun to (that: B of kotlin.TuplesKt.to): kotlin.Pair [infix] declared in kotlin.TuplesKt' type=kotlin.Pair origin=null : kotlin.Int : kotlin.Int $receiver: CONST Int type=kotlin.Int value=1 that: CONST Int type=kotlin.Int value=3 GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null CALL 'private final fun flag (: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, code: kotlin.String, vararg locations: kotlin.Pair): kotlin.Unit [inline] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never' type=kotlin.Unit origin=null $this: TYPE_OP type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=IMPLICIT_CAST typeOperand=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never visibility:public/*package*/ [final]' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null receiver: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null $receiver: GET_VAR '$this$braceTests: kotlin.collections.MutableList declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.collections.MutableList origin=null : GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements origin=null code: CONST String type=kotlin.String value="if (true) { println() } else if (true) println() else if (true) println() else println()" locations: BLOCK type=kotlin.Array> origin=null VAR IR_TEMPORARY_VARIABLE name:tmp0 type:kotlin.Array> [val] CALL 'public final fun arrayOfNulls (size: kotlin.Int): kotlin.Array declared in kotlin' type=kotlin.Array> origin=null : kotlin.Pair size: CONST Int type=kotlin.Int value=1 CALL 'public final fun set (index: kotlin.Int, value: T of kotlin.Array): kotlin.Unit [operator] declared in kotlin.Array' type=kotlin.Unit origin=null $this: GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null index: CONST Int type=kotlin.Int value=0 value: CALL 'public final fun to (that: B of kotlin.TuplesKt.to): kotlin.Pair [infix] declared in kotlin.TuplesKt' type=kotlin.Pair origin=null : kotlin.Int : kotlin.Int $receiver: CONST Int type=kotlin.Int value=1 that: CONST Int type=kotlin.Int value=3 GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null FIELD FIELD_FOR_CAPTURED_VALUE name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never visibility:public/*package*/ [final] FUN BRIDGE name:invoke visibility:public modality:OPEN <> ($this:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged., p1:kotlin.Any?, p2:kotlin.Any?) returnType:kotlin.Any? $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. VALUE_PARAMETER BRIDGE name:p1 index:0 type:kotlin.Any? VALUE_PARAMETER BRIDGE name:p2 index:1 type:kotlin.Any? EXPRESSION_BODY CALL 'public final fun invoke (: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, $this$braceTests: kotlin.collections.MutableList): kotlin.Unit declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged.' type=kotlin.Unit origin=BRIDGE_DELEGATION $this: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null : TYPE_OP type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements origin=IMPLICIT_CAST typeOperand=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements GET_VAR 'p1: kotlin.Any? declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Any? origin=null $this$braceTests: TYPE_OP type=kotlin.collections.MutableList<*> origin=IMPLICIT_CAST typeOperand=kotlin.collections.MutableList<*> GET_VAR 'p2: kotlin.Any? declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Any? origin=null CONSTRUCTOR_CALL 'public/*package*/ constructor (this$0: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never) [primary] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged.' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null this$0: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:49) at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate$default(FunctionCodegen.kt:41) at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generateMethodNode(ClassCodegen.kt:411) at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generateMethod(ClassCodegen.kt:428) at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generate(ClassCodegen.kt:171) at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generate(ClassCodegen.kt:184) at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generate(ClassCodegen.kt:184) at org.jetbrains.kotlin.backend.jvm.FileCodegen.lower(JvmPhases.kt:44) at org.jetbrains.kotlin.backend.common.phaser.FileLoweringPhaseAdapter.invoke(PhaseBuilders.kt:120) at org.jetbrains.kotlin.backend.common.phaser.FileLoweringPhaseAdapter.invoke(PhaseBuilders.kt:116) at org.jetbrains.kotlin.backend.common.phaser.NamedCompilerPhase.invoke(CompilerPhase.kt:96) at org.jetbrains.kotlin.backend.common.phaser.PerformByIrFilePhase.invokeSequential(performByIrFile.kt:65) ... 40 more Caused by: java.lang.RuntimeException: Exception while generating code for: FUN name:invoke visibility:public modality:FINAL <> ($this:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged., :io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, $this$braceTests:kotlin.collections.MutableList) returnType:kotlin.Unit overridden: public abstract fun invoke (p1: P1 of kotlin.Function2, p2: P2 of kotlin.Function2): R of kotlin.Function2 [operator] declared in kotlin.Function2 $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. VALUE_PARAMETER name: index:0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements VALUE_PARAMETER name:$this$braceTests index:1 type:kotlin.collections.MutableList BLOCK_BODY CALL 'private final fun flag (: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, code: kotlin.String, vararg locations: kotlin.Pair): kotlin.Unit [inline] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never' type=kotlin.Unit origin=null $this: TYPE_OP type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=IMPLICIT_CAST typeOperand=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never visibility:public/*package*/ [final]' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null receiver: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null $receiver: GET_VAR '$this$braceTests: kotlin.collections.MutableList declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.collections.MutableList origin=null : GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements origin=null code: CONST String type=kotlin.String value="if (true) { println() }" locations: BLOCK type=kotlin.Array> origin=null VAR IR_TEMPORARY_VARIABLE name:tmp0 type:kotlin.Array> [val] CALL 'public final fun arrayOfNulls (size: kotlin.Int): kotlin.Array declared in kotlin' type=kotlin.Array> origin=null : kotlin.Pair size: CONST Int type=kotlin.Int value=1 CALL 'public final fun set (index: kotlin.Int, value: T of kotlin.Array): kotlin.Unit [operator] declared in kotlin.Array' type=kotlin.Unit origin=null $this: GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null index: CONST Int type=kotlin.Int value=0 value: CALL 'public final fun to (that: B of kotlin.TuplesKt.to): kotlin.Pair [infix] declared in kotlin.TuplesKt' type=kotlin.Pair origin=null : kotlin.Int : kotlin.Int $receiver: CONST Int type=kotlin.Int value=1 that: CONST Int type=kotlin.Int value=3 GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null CALL 'private final fun flag (: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, code: kotlin.String, vararg locations: kotlin.Pair): kotlin.Unit [inline] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never' type=kotlin.Unit origin=null $this: TYPE_OP type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=IMPLICIT_CAST typeOperand=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never visibility:public/*package*/ [final]' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null receiver: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null $receiver: GET_VAR '$this$braceTests: kotlin.collections.MutableList declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.collections.MutableList origin=null : GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements origin=null code: CONST String type=kotlin.String value="if (true) { println() } else println()" locations: BLOCK type=kotlin.Array> origin=null VAR IR_TEMPORARY_VARIABLE name:tmp0 type:kotlin.Array> [val] CALL 'public final fun arrayOfNulls (size: kotlin.Int): kotlin.Array declared in kotlin' type=kotlin.Array> origin=null : kotlin.Pair size: CONST Int type=kotlin.Int value=1 CALL 'public final fun set (index: kotlin.Int, value: T of kotlin.Array): kotlin.Unit [operator] declared in kotlin.Array' type=kotlin.Unit origin=null $this: GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null index: CONST Int type=kotlin.Int value=0 value: CALL 'public final fun to (that: B of kotlin.TuplesKt.to): kotlin.Pair [infix] declared in kotlin.TuplesKt' type=kotlin.Pair origin=null : kotlin.Int : kotlin.Int $receiver: CONST Int type=kotlin.Int value=1 that: CONST Int type=kotlin.Int value=3 GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null CALL 'private final fun flag (: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, code: kotlin.String, vararg locations: kotlin.Pair): kotlin.Unit [inline] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never' type=kotlin.Unit origin=null $this: TYPE_OP type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=IMPLICIT_CAST typeOperand=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never visibility:public/*package*/ [final]' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null receiver: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null $receiver: GET_VAR '$this$braceTests: kotlin.collections.MutableList declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.collections.MutableList origin=null : GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements origin=null code: CONST String type=kotlin.String value="if (true) { println() } else if (true) println()" locations: BLOCK type=kotlin.Array> origin=null VAR IR_TEMPORARY_VARIABLE name:tmp0 type:kotlin.Array> [val] CALL 'public final fun arrayOfNulls (size: kotlin.Int): kotlin.Array declared in kotlin' type=kotlin.Array> origin=null : kotlin.Pair size: CONST Int type=kotlin.Int value=1 CALL 'public final fun set (index: kotlin.Int, value: T of kotlin.Array): kotlin.Unit [operator] declared in kotlin.Array' type=kotlin.Unit origin=null $this: GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null index: CONST Int type=kotlin.Int value=0 value: CALL 'public final fun to (that: B of kotlin.TuplesKt.to): kotlin.Pair [infix] declared in kotlin.TuplesKt' type=kotlin.Pair origin=null : kotlin.Int : kotlin.Int $receiver: CONST Int type=kotlin.Int value=1 that: CONST Int type=kotlin.Int value=3 GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null CALL 'private final fun flag (: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, code: kotlin.String, vararg locations: kotlin.Pair): kotlin.Unit [inline] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never' type=kotlin.Unit origin=null $this: TYPE_OP type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=IMPLICIT_CAST typeOperand=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never visibility:public/*package*/ [final]' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null receiver: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null $receiver: GET_VAR '$this$braceTests: kotlin.collections.MutableList declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.collections.MutableList origin=null : GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements origin=null code: CONST String type=kotlin.String value="if (true) { println() } else if (true) println() else println()" locations: BLOCK type=kotlin.Array> origin=null VAR IR_TEMPORARY_VARIABLE name:tmp0 type:kotlin.Array> [val] CALL 'public final fun arrayOfNulls (size: kotlin.Int): kotlin.Array declared in kotlin' type=kotlin.Array> origin=null : kotlin.Pair size: CONST Int type=kotlin.Int value=1 CALL 'public final fun set (index: kotlin.Int, value: T of kotlin.Array): kotlin.Unit [operator] declared in kotlin.Array' type=kotlin.Unit origin=null $this: GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null index: CONST Int type=kotlin.Int value=0 value: CALL 'public final fun to (that: B of kotlin.TuplesKt.to): kotlin.Pair [infix] declared in kotlin.TuplesKt' type=kotlin.Pair origin=null : kotlin.Int : kotlin.Int $receiver: CONST Int type=kotlin.Int value=1 that: CONST Int type=kotlin.Int value=3 GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null CALL 'private final fun flag (: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, code: kotlin.String, vararg locations: kotlin.Pair): kotlin.Unit [inline] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never' type=kotlin.Unit origin=null $this: TYPE_OP type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=IMPLICIT_CAST typeOperand=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never GET_FIELD 'FIELD FIELD_FOR_CAPTURED_VALUE name:this$0 type:io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never visibility:public/*package*/ [final]' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never origin=null receiver: GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged. origin=null $receiver: GET_VAR '$this$braceTests: kotlin.collections.MutableList declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.collections.MutableList origin=null : GET_VAR ': io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements origin=null code: CONST String type=kotlin.String value="if (true) { println() } else if (true) println() else if (true) println() else println()" locations: BLOCK type=kotlin.Array> origin=null VAR IR_TEMPORARY_VARIABLE name:tmp0 type:kotlin.Array> [val] CALL 'public final fun arrayOfNulls (size: kotlin.Int): kotlin.Array declared in kotlin' type=kotlin.Array> origin=null : kotlin.Pair size: CONST Int type=kotlin.Int value=1 CALL 'public final fun set (index: kotlin.Int, value: T of kotlin.Array): kotlin.Unit [operator] declared in kotlin.Array' type=kotlin.Unit origin=null $this: GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null index: CONST Int type=kotlin.Int value=0 value: CALL 'public final fun to (that: B of kotlin.TuplesKt.to): kotlin.Pair [infix] declared in kotlin.TuplesKt' type=kotlin.Pair origin=null : kotlin.Int : kotlin.Int $receiver: CONST Int type=kotlin.Int value=1 that: CONST Int type=kotlin.Int value=3 GET_VAR 'val tmp0: kotlin.Array> [val] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never.partially extra braces are flagged..invoke' type=kotlin.Array> origin=null at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:49) at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate$default(FunctionCodegen.kt:41) at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generateMethodNode(ClassCodegen.kt:411) at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generateMethod(ClassCodegen.kt:428) at org.jetbrains.kotlin.backend.jvm.codegen.ClassCodegen.generate(ClassCodegen.kt:171) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitClass(ExpressionCodegen.kt:861) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitClass(ExpressionCodegen.kt:135) at org.jetbrains.kotlin.ir.declarations.IrClass.accept(IrClass.kt:64) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitStatementContainer(ExpressionCodegen.kt:457) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitContainerExpression(ExpressionCodegen.kt:470) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitContainerExpression(ExpressionCodegen.kt:135) at org.jetbrains.kotlin.ir.visitors.IrElementVisitor$DefaultImpls.visitBlock(IrElementVisitor.kt:192) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitBlock(ExpressionCodegen.kt:398) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitBlock(ExpressionCodegen.kt:135) at org.jetbrains.kotlin.ir.expressions.IrBlock.accept(IrBlock.kt:22) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.gen(ExpressionCodegen.kt:218) at org.jetbrains.kotlin.backend.jvm.codegen.IrCallGenerator.genValueAndPut(IrCallGenerator.kt:50) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitCall$handleValueParameter(ExpressionCodegen.kt:500) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitCall(ExpressionCodegen.kt:514) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitCall(ExpressionCodegen.kt:135) at org.jetbrains.kotlin.ir.expressions.IrCall.accept(IrCall.kt:25) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitReturn(ExpressionCodegen.kt:908) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitReturn(ExpressionCodegen.kt:135) at org.jetbrains.kotlin.ir.expressions.IrReturn.accept(IrReturn.kt:25) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitStatementContainer(ExpressionCodegen.kt:457) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitBlockBody(ExpressionCodegen.kt:461) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitBlockBody(ExpressionCodegen.kt:135) at org.jetbrains.kotlin.ir.expressions.IrBlockBody.accept(IrBlockBody.kt:24) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.generate(ExpressionCodegen.kt:241) at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.doGenerate(FunctionCodegen.kt:120) at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:45) ... 51 more Caused by: org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: Couldn't inline method call: CALL 'private final fun flag (: io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements, code: kotlin.String, vararg locations: kotlin.Pair): kotlin.Unit [inline] declared in io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.singleLine.=never' type=kotlin.Unit origin=null Method: flag (Lio/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements;Ljava/util/List;Ljava/lang/String;[Lkotlin/Pair;)V: // annotable parameter count: 4 (visible) // annotable parameter count: 4 (invisible) L0 ICONST_0 ISTORE 5 L1 L2 LINENUMBER 173 L2 ALOAD 2 L3 LINENUMBER 174 L3 NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder. ()V LDC "flags " INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 4 ASTORE 7 INVOKESTATIC kotlin/jvm/internal/InlineMarker.beforeInlineCall ()V NOP L4 ICONST_0 ISTORE 8 L5 LINENUMBER 327 L5 ALOAD 7 ASTORE 9 NEW java/util/ArrayList DUP ALOAD 7 ARRAYLENGTH INVOKESPECIAL java/util/ArrayList. (I)V CHECKCAST java/util/Collection ASTORE 10 L6 ICONST_0 ISTORE 11 L7 LINENUMBER 328 L7 ICONST_0 ISTORE 12 ALOAD 9 ARRAYLENGTH ISTORE 13 L8 ILOAD 12 ILOAD 13 IF_ICMPGE L9 ALOAD 9 ILOAD 12 AALOAD ASTORE 14 L10 LINENUMBER 329 L10 ALOAD 10 ALOAD 14 CHECKCAST kotlin/Pair ASTORE 15 INVOKESTATIC kotlin/jvm/internal/InlineMarker.beforeInlineCall ()V L11 ICONST_0 ISTORE 16 L12 LINENUMBER 174 L12 NEW io/gitlab/arturbosch/detekt/api/TextLocation DUP ALOAD 15 INVOKEVIRTUAL kotlin/Pair.getFirst ()Ljava/lang/Object; CHECKCAST java/lang/Number INVOKEVIRTUAL java/lang/Number.intValue ()I ALOAD 15 INVOKEVIRTUAL kotlin/Pair.getSecond ()Ljava/lang/Object; CHECKCAST java/lang/Number INVOKEVIRTUAL java/lang/Number.intValue ()I INVOKESPECIAL io/gitlab/arturbosch/detekt/api/TextLocation. (II)V NOP GOTO L13 L14 L15 L13 INVOKESTATIC kotlin/jvm/internal/InlineMarker.afterInlineCall ()V L16 LINENUMBER 329 L16 INVOKEINTERFACE java/util/Collection.add (Ljava/lang/Object;)Z (itf) POP L17 LINENUMBER 328 L17 IINC 12 1 GOTO L8 L9 LINENUMBER 330 L9 ALOAD 10 L18 CHECKCAST java/util/List L19 LINENUMBER 327 L19 NOP GOTO L20 L21 L22 L20 INVOKESTATIC kotlin/jvm/internal/InlineMarker.afterInlineCall ()V L23 LINENUMBER 174 L23 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/Object;)Ljava/lang/StringBuilder; LDC " in `" INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 3 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; BIPUSH 96 INVOKEVIRTUAL java/lang/StringBuilder.append (C)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; NEW io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec$singleLine$=never$flag$2 DUP ALOAD 0 GETFIELD io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec$singleLine$=never.this$0 : Lio/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec$singleLine; GETFIELD io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec$singleLine.this$0 : Lio/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec; ALOAD 1 ALOAD 3 ALOAD 4 INVOKESPECIAL io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec$singleLine$=never$flag$2. (Lio/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec;Lio/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements;Ljava/lang/String;[Lkotlin/Pair;)V CHECKCAST org/junit/jupiter/api/function/Executable INVOKESTATIC org/junit/jupiter/api/DynamicTest.dynamicTest (Ljava/lang/String;Lorg/junit/jupiter/api/function/Executable;)Lorg/junit/jupiter/api/DynamicTest; ASTORE 6 L24 ALOAD 6 LDC "class BracesOnIfStatemen\u2026 listOf(option)\n}" INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue (Ljava/lang/Object;Ljava/lang/String;)V ALOAD 6 L25 L26 LINENUMBER 173 L26 INVOKEINTERFACE java/util/List.add (Ljava/lang/Object;)Z (itf) POP L27 LINENUMBER 181 L27 RETURN L28 LOCALVARIABLE $i$a$-map-BracesOnIfStatementsSpec$singleLine$=never$flag$1 I L12 L15 16 LOCALVARIABLE it Lkotlin/Pair; L11 L15 15 LOCALVARIABLE item$iv$iv Ljava/lang/Object; L10 L17 14 LOCALVARIABLE $i$f$mapTo I L7 L18 11 LOCALVARIABLE $this$mapTo$iv$iv [Ljava/lang/Object; L6 L18 9 LOCALVARIABLE destination$iv$iv Ljava/util/Collection; L6 L18 10 LOCALVARIABLE $i$f$map I L5 L22 8 LOCALVARIABLE $this$map$iv [Ljava/lang/Object; L4 L22 7 LOCALVARIABLE $i$f$flag I L1 L28 5 LOCALVARIABLE this Lio/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec$singleLine$=never; L0 L28 0 LOCALVARIABLE $this$flag Ljava/util/List; L0 L28 2 LOCALVARIABLE Lio/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements; L0 L28 1 LOCALVARIABLE code Ljava/lang/String; L0 L28 3 LOCALVARIABLE locations [Lkotlin/Pair; L0 L28 4 MAXSTACK = 8 MAXLOCALS = 17 File is unknown The root cause java.lang.NullPointerException was thrown at: org.jetbrains.kotlin.codegen.inline.Parameters.getDeclarationSlot(Parameters.kt:57) at org.jetbrains.kotlin.codegen.inline.InlineCodegen.performInline(InlineCodegen.kt:63) at org.jetbrains.kotlin.backend.jvm.codegen.IrInlineCodegen.genInlineCall(IrInlineCodegen.kt:163) at org.jetbrains.kotlin.backend.jvm.codegen.IrInlineCallGenerator.genCall(IrInlineCallGenerator.kt:31) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitCall(ExpressionCodegen.kt:522) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitCall(ExpressionCodegen.kt:135) at org.jetbrains.kotlin.ir.expressions.IrCall.accept(IrCall.kt:25) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitStatementContainer(ExpressionCodegen.kt:457) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitBlockBody(ExpressionCodegen.kt:461) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.visitBlockBody(ExpressionCodegen.kt:135) at org.jetbrains.kotlin.ir.expressions.IrBlockBody.accept(IrBlockBody.kt:24) at org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen.generate(ExpressionCodegen.kt:241) at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.doGenerate(FunctionCodegen.kt:120) at org.jetbrains.kotlin.backend.jvm.codegen.FunctionCodegen.generate(FunctionCodegen.kt:45) ... 81 more Caused by: java.lang.NullPointerException at org.jetbrains.kotlin.codegen.inline.Parameters.getDeclarationSlot(Parameters.kt:57) at org.jetbrains.kotlin.codegen.inline.LocalVarRemapper.(LocalVarRemapper.kt:35) at org.jetbrains.kotlin.codegen.inline.InlineCodegen.inlineCall(InlineCodegen.kt:112) at org.jetbrains.kotlin.codegen.inline.InlineCodegen.performInline(InlineCodegen.kt:50) ... 93 more --- detekt-rules-style/build.gradle.kts | 4 + .../rules/style/BracesOnIfStatementsSpec.kt | 276 ++++++++++-------- .../arturbosch/detekt/test/RuleExtensions.kt | 2 +- 3 files changed, 163 insertions(+), 119 deletions(-) diff --git a/detekt-rules-style/build.gradle.kts b/detekt-rules-style/build.gradle.kts index eeaa48ff06a..d45e4e14d19 100644 --- a/detekt-rules-style/build.gradle.kts +++ b/detekt-rules-style/build.gradle.kts @@ -11,3 +11,7 @@ dependencies { testImplementation(libs.mockk) testImplementation(libs.assertj) } + +tasks.withType().configureEach { + compilerOptions.freeCompilerArgs.add("-Xcontext-receivers") +} diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 60e550eba18..d07262cbfb2 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -1,12 +1,15 @@ -@file:Suppress("ClassName", "PrivatePropertyName") +@file:Suppress("ClassName", "PrivatePropertyName", "ClassOrdering", "VariableNaming") package io.gitlab.arturbosch.detekt.rules.style import io.gitlab.arturbosch.detekt.api.Finding +import io.gitlab.arturbosch.detekt.api.TextLocation import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.junit.jupiter.api.DynamicContainer.dynamicContainer import org.junit.jupiter.api.DynamicNode +import org.junit.jupiter.api.DynamicTest import org.junit.jupiter.api.DynamicTest.dynamicTest import org.junit.jupiter.api.Nested import org.junit.jupiter.api.TestFactory @@ -117,134 +120,171 @@ class BracesOnIfStatementsSpec { @Nested inner class singleLine { - - @Nested - inner class `=always` { - - private val singleLine = BracesOnIfStatements.BracePolicy.Always.config - - @TestFactory - fun `missing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { - val findings = compileAndLintInF(NO_BRACES_SINGLE) - - assertThat(findings).hasTextLocations(*NO_BRACES_SINGLE_LOCATIONS) - } - - @TestFactory - fun `existing braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { - val findings = compileAndLintInF(ALL_BRACES_SINGLE) - - assertThat(findings).isEmpty() - } - - @TestFactory - fun `partially missing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { - val findings = compileAndLintInF(IF_BRACES_SINGLE) - - assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_NO_BRACE_LOCATIONS) - } - } +// +// @Nested +// inner class `=always` { +// +// private val singleLine = BracesOnIfStatements.BracePolicy.Always.config +// +// @TestFactory +// fun `missing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { +// val findings = compileAndLintInF(NO_BRACES_SINGLE) +// +// assertThat(findings).hasTextLocations(*NO_BRACES_SINGLE_LOCATIONS) +// } +// +// @TestFactory +// fun `existing braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { +// val findings = compileAndLintInF(ALL_BRACES_SINGLE) +// +// assertThat(findings).isEmpty() +// } +// +// @TestFactory +// fun `partially missing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { +// val findings = compileAndLintInF(IF_BRACES_SINGLE) +// +// assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_NO_BRACE_LOCATIONS) +// } +// } @Nested inner class `=never` { + val NOTHING = arrayOf>() private val singleLine = BracesOnIfStatements.BracePolicy.Never.config - @TestFactory - fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { - val findings = compileAndLintInF(NO_BRACES_SINGLE) - - assertThat(findings).isEmpty() - } - - @TestFactory - fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { - val findings = compileAndLintInF(ALL_BRACES_SINGLE) - - assertThat(findings).hasTextLocations(*ALL_BRACES_SINGLE_LOCATIONS) - } - - @TestFactory - fun `partially extra braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { - val findings = compileAndLintInF(IF_BRACES_SINGLE) - - assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_BRACE_LOCATIONS) - } - } - - @Nested - inner class `=necessary` { - - private val singleLine = BracesOnIfStatements.BracePolicy.Necessary.config - - @TestFactory - fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { - val findings = compileAndLintInF(NO_BRACES_SINGLE) - - assertThat(findings).isEmpty() - } - - @TestFactory - fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { - val findings = compileAndLintInF(ALL_BRACES_SINGLE) - - assertThat(findings).hasTextLocations(*ALL_BRACES_SINGLE_LOCATIONS) - } - - @TestFactory - fun `partially extra braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { - val findings = compileAndLintInF(IF_BRACES_SINGLE) - - assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_BRACE_LOCATIONS) - } - } - - @Nested - inner class `=consistent` { - - private val singleLine = BracesOnIfStatements.BracePolicy.Consistent.config - - @TestFactory - fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { - val findings = compileAndLintInF(NO_BRACES_SINGLE) - - assertThat(findings).isEmpty() +// @TestFactory +// fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { +// val findings = compileAndLintInF(NO_BRACES_SINGLE) +// +// assertThat(findings).isEmpty() +// } +// +// @TestFactory +// fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { +// val findings = compileAndLintInF(ALL_BRACES_SINGLE) +// +// assertThat(findings).hasTextLocations(*ALL_BRACES_SINGLE_LOCATIONS) +// } + + context(BracesOnIfStatements) + private inline fun MutableList.flag(code: String, vararg locations: Pair) { + add( + dynamicTest("flags ${locations.map { TextLocation(it.first, it.second) }} in `$code`") { + val findings = compileAndLintInF(code) + + assertThat(findings).hasTextLocations(*locations.map { it.first + 13 to it.second + 13 } + .toTypedArray()) + } + ) } @TestFactory - fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { - val findings = compileAndLintInF(ALL_BRACES_SINGLE) - - assertThat(findings).isEmpty() + fun `partially extra braces are flagged`() = braceTests("never", NOT_RELEVANT) { + flag("if (true) { println() }", 1 to 3) + flag("if (true) { println() } else println()", 1 to 3) + flag("if (true) { println() } else if (true) println()", 1 to 3) + flag("if (true) { println() } else if (true) println() else println()", 1 to 3) + flag( + """ + if (true) { println() } else if (true) println() else if (true) println() else println() + """.trimIndent(), + 1 to 3, + ) } @TestFactory - fun `partial braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { - val findings = compileAndLintInF(IF_BRACES_SINGLE) - - assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_BRACE_LOCATIONS.drop(1).toTypedArray()) + fun `partially missing braces are flagged`() = braceTests("always", NOT_RELEVANT) { + flag("if (true) { println() }", *NOTHING) + flag("if (true) { println() } else println()", 24 to 28) + flag("if (true) { println() } else if (true) println()", 29 to 31) + flag("if (true) { println() } else if (true) println() else println()", 29 to 31, 49 to 51) + flag( + """ + if (true) { println() } + else if (true) + println() + else if (true) println() else println() + """.trimIndent(), + 29 to 31, 49 to 51, 74 to 78, + ) } } +// +// @Nested +// inner class `=necessary` { +// +// private val singleLine = BracesOnIfStatements.BracePolicy.Necessary.config +// +// @TestFactory +// fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { +// val findings = compileAndLintInF(NO_BRACES_SINGLE) +// +// assertThat(findings).isEmpty() +// } +// +// @TestFactory +// fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { +// val findings = compileAndLintInF(ALL_BRACES_SINGLE) +// +// assertThat(findings).hasTextLocations(*ALL_BRACES_SINGLE_LOCATIONS) +// } +// +// @TestFactory +// fun `partially extra braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { +// val findings = compileAndLintInF(IF_BRACES_SINGLE) +// +// assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_BRACE_LOCATIONS) +// } +// } +// +// @Nested +// inner class `=consistent` { +// +// private val singleLine = BracesOnIfStatements.BracePolicy.Consistent.config +// +// @TestFactory +// fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { +// val findings = compileAndLintInF(NO_BRACES_SINGLE) +// +// assertThat(findings).isEmpty() +// } +// +// @TestFactory +// fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { +// val findings = compileAndLintInF(ALL_BRACES_SINGLE) +// +// assertThat(findings).isEmpty() +// } +// +// @TestFactory +// fun `partial braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { +// val findings = compileAndLintInF(IF_BRACES_SINGLE) +// +// assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_BRACE_LOCATIONS.drop(1).toTypedArray()) +// } +// } } - @TestFactory - fun `whens are not flagged`() = braceTests(NOT_RELEVANT, NOT_RELEVANT) { - val findings = compileAndLintInF( - """ - when (true) { - true -> println() - else -> println() - } - """.trimIndent() - ) - - assertThat(findings).isEmpty() - } +// @TestFactory +// fun `whens are not flagged`() = braceTests(NOT_RELEVANT, NOT_RELEVANT) { +// val findings = compileAndLintInF( +// """ +// when (true) { +// true -> println() +// else -> println() +// } +// """.trimIndent() +// ) +// +// assertThat(findings).isEmpty() +// } private fun braceTests( singleLine: String, multiLine: String, - test: BracesOnIfStatements.() -> Unit + test: context(BracesOnIfStatements) MutableList.() -> Unit ): List { val singleOptions = options(singleLine) val multiOptions = options(multiLine) @@ -252,24 +292,24 @@ class BracesOnIfStatementsSpec { singleOptions.size > 1 && multiOptions.size > 1 -> singleOptions.flatMap { singleLineOption -> multiOptions.map { multiLineOption -> - dynamicTest("singleLine=${singleLineOption}, multiLine=${multiLineOption}") { - createSubject(singleLineOption, multiLineOption).test() - } + val tests = mutableListOf() + test(createSubject(singleLineOption, multiLineOption), tests) + dynamicContainer("singleLine=${singleLineOption}, multiLine=${multiLineOption}", tests) } } singleOptions.size > 1 && multiOptions.size == 1 -> singleOptions.map { singleLineOption -> - dynamicTest("singleLine=${singleLineOption}}") { - createSubject(singleLineOption, multiOptions.single()).test() - } + val tests = mutableListOf() + test(createSubject(singleLineOption, multiOptions.single()), tests) + dynamicContainer("singleLine=${singleLineOption}, multiLine=${multiOptions.single()}", tests) } singleOptions.size == 1 && multiOptions.size > 1 -> multiOptions.map { multiLineOption -> - dynamicTest("multiLine=${multiLineOption}") { - createSubject(singleOptions.single(), multiLineOption).test() - } + val tests = mutableListOf() + test(createSubject(singleOptions.single(), multiLineOption), tests) + dynamicContainer("singleLine=${singleOptions.single()}, multiLine=${multiLineOption}", tests) } else -> diff --git a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/RuleExtensions.kt b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/RuleExtensions.kt index 917b4ac7b48..ef327d2d9c9 100644 --- a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/RuleExtensions.kt +++ b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/RuleExtensions.kt @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl import java.nio.file.Path private val shouldCompileTestSnippets: Boolean = - System.getProperty("compile-test-snippets", "false")!!.toBoolean() + System.getProperty("compile-test-snippets", "true")!!.toBoolean() fun BaseRule.compileAndLint(@Language("kotlin") content: String): List { if (shouldCompileTestSnippets) { From 0fb41e15238a08ab44ff44d1dae84ad2f1e3bbab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Thu, 26 Jan 2023 14:50:52 +0000 Subject: [PATCH 08/33] fixes --- .../rules/style/BracesOnIfStatementsSpec.kt | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index d07262cbfb2..3b3560d5188 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -169,7 +169,7 @@ class BracesOnIfStatementsSpec { // } context(BracesOnIfStatements) - private inline fun MutableList.flag(code: String, vararg locations: Pair) { + private fun MutableList.flag(code: String, vararg locations: Pair) { add( dynamicTest("flags ${locations.map { TextLocation(it.first, it.second) }} in `$code`") { val findings = compileAndLintInF(code) @@ -197,17 +197,14 @@ class BracesOnIfStatementsSpec { @TestFactory fun `partially missing braces are flagged`() = braceTests("always", NOT_RELEVANT) { flag("if (true) { println() }", *NOTHING) - flag("if (true) { println() } else println()", 24 to 28) - flag("if (true) { println() } else if (true) println()", 29 to 31) - flag("if (true) { println() } else if (true) println() else println()", 29 to 31, 49 to 51) + flag("if (true) { println() } else println()", 25 to 29) + flag("if (true) { println() } else if (true) println()", 30 to 32) + flag("if (true) { println() } else if (true) println() else println()", 30 to 32, 50 to 52) flag( """ - if (true) { println() } - else if (true) - println() - else if (true) println() else println() + if (true) { println() } else if (true) println() else if (true) println() else println() """.trimIndent(), - 29 to 31, 49 to 51, 74 to 78, + 30 to 32, 50 to 52, 75 to 79, ) } } From 82adf9f598d285e16de276b29b38abdc9593bd71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Thu, 26 Jan 2023 16:09:49 +0000 Subject: [PATCH 09/33] New approach --- detekt-rules-style/build.gradle.kts | 4 - .../rules/style/BracesOnIfStatementsSpec.kt | 424 +++++++++--------- .../detekt/test/FindingsAssertions.kt | 17 + .../arturbosch/detekt/test/RuleExtensions.kt | 2 +- 4 files changed, 220 insertions(+), 227 deletions(-) diff --git a/detekt-rules-style/build.gradle.kts b/detekt-rules-style/build.gradle.kts index d45e4e14d19..eeaa48ff06a 100644 --- a/detekt-rules-style/build.gradle.kts +++ b/detekt-rules-style/build.gradle.kts @@ -11,7 +11,3 @@ dependencies { testImplementation(libs.mockk) testImplementation(libs.assertj) } - -tasks.withType().configureEach { - compilerOptions.freeCompilerArgs.add("-Xcontext-receivers") -} diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 3b3560d5188..5d19bd274b4 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -1,214 +1,115 @@ -@file:Suppress("ClassName", "PrivatePropertyName", "ClassOrdering", "VariableNaming") +@file:Suppress("ClassName", "CommentOverPrivateProperty") package io.gitlab.arturbosch.detekt.rules.style -import io.gitlab.arturbosch.detekt.api.Finding import io.gitlab.arturbosch.detekt.api.TextLocation +import io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements.BracePolicy import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint import org.junit.jupiter.api.DynamicContainer.dynamicContainer import org.junit.jupiter.api.DynamicNode -import org.junit.jupiter.api.DynamicTest import org.junit.jupiter.api.DynamicTest.dynamicTest import org.junit.jupiter.api.Nested import org.junit.jupiter.api.TestFactory -private val NO_BRACES_SINGLE: String = """ - if (true) println() - if (true) println() else println() - if (true) println() else if println() - if (true) println() else if println() else println() - if (true) println() else if println() else if println() else println() -""".trimIndent() +/** + * Not relevant means, that it should be covered, but for that specific test case the value doesn't matter. + * This needs to be covered still, to make sure it never becomes relevant. + * In this rule, configuration options should be separate for each config, not coupled with each other. + */ +private const val NOT_RELEVANT: String = "*" -private val NO_BRACES_SINGLE_LOCATIONS: Array> = arrayOf( - 14 to 16, - 38 to 40, - 58 to 62, - 77 to 79, - 102 to 104, - 119 to 121, - 144 to 146, - 157 to 161, - 176 to 178, - 201 to 203, - 219 to 221, - 232 to 236, -) +/** + * Nothing is expected to be flagged as a finding. + */ +private val NOTHING: Array> = emptyArray() -private val ALL_BRACES_SINGLE: String = """ - if (true) { println() } - if (true) { println() } else { println() } - if (true) { println() } else if { println() } - if (true) { println() } else if { println() } else { println() } - if (true) { println() } else if { println() } else if { println() } else { println() } -""".trimIndent() - -private val ALL_BRACES_SINGLE_LOCATIONS: Array> = arrayOf( - 14 to 16, - 42 to 44, - 66 to 70, - 89 to 91, - 118 to 120, - 139 to 141, - 168 to 170, - 185 to 189, - 208 to 210, - 237 to 239, - 259 to 261, - 276 to 280, -) - -private val IF_BRACES_SINGLE: String = """ - if (true) { println() } - if (true) { println() } else println() - if (true) { println() } else if println() - if (true) { println() } else if println() else println() - if (true) { println() } else if println() else if println() else println() -""".trimIndent() - -private val IF_BRACES_SINGLE_BRACE_LOCATIONS: Array> = arrayOf( - 14 to 16, - 42 to 44, - 85 to 87, - 131 to 133, - 192 to 194, -) - -private val IF_BRACES_SINGLE_NO_BRACE_LOCATIONS: Array> = arrayOf( - 66 to 70, - 114 to 116, - 160 to 162, - 173 to 177, - 221 to 223, - 239 to 241, - 252 to 256, -) +/** + * Note: this class makes extensive use of dynamic tests and containers, few tips for maintenance: + * * Numbers are made relative to the code snippet rather than the whole code passed to Kotlin compiler. [compileAndLintInF]. + * * To debug a specific test case, remove all other test cases from the same method. + * * Test coverage is added for all possible configuration options, see [NOT_RELEVANT]. + */ +class BracesOnIfStatementsSpec { -private val ELSE_BRACES_SINGLE: String = """ - if (true) println() - if (true) println() else { println() } - if (true) println() else if println() - if (true) println() else if println() else { println() } - if (true) println() else if println() else if println() - if (true) println() else if println() else if println() else { println() } -""".trimIndent() + @Nested + inner class singleLine { -private const val NOT_RELEVANT = "*" + @Nested + inner class `=always` { -class BracesOnIfStatementsSpec { + private fun flag(code: String, vararg locations: Pair) = + testCombinations(BracePolicy.Always.config, NOT_RELEVANT, code, *locations) - private fun createSubject(singleLine: String, multiLine: String): BracesOnIfStatements { - val config = TestConfig( - mapOf( - "singleLine" to singleLine, - "multiLine" to multiLine + @TestFactory + fun `missing braces are flagged`() = listOf( + flag("if (true) println()", 0 to 2), + flag("if (true) println() else println()", 0 to 2), + flag("if (true) println() else if (true) println()", 0 to 2), + flag("if (true) println() else if (true) println() else println()", 0 to 2), + flag("if (true) println() else if println() else if (true) println() else println()", 0 to 2), ) - ) - return BracesOnIfStatements(config) - } - private fun BracesOnIfStatements.compileAndLintInF(code: String): List = - compileAndLint( - """ - fun f() { - ${code.prependIndent(" ").trimStart()} - } - """.trimIndent() - ) + @TestFactory + fun `existing braces are accepted`() = listOf( + flag("if (true) { println() }", *NOTHING), + flag("if (true) { println() } else { println() }", *NOTHING), + flag("if (true) { println() } else if { println() }", *NOTHING), + flag("if (true) { println() } else if { println() } else { println() }", *NOTHING), + flag( + "if (true) { println() } else if { println() } else if { println() } else { println() }", + *NOTHING + ), + ) - @Nested - inner class singleLine { -// -// @Nested -// inner class `=always` { -// -// private val singleLine = BracesOnIfStatements.BracePolicy.Always.config -// -// @TestFactory -// fun `missing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF(NO_BRACES_SINGLE) -// -// assertThat(findings).hasTextLocations(*NO_BRACES_SINGLE_LOCATIONS) -// } -// -// @TestFactory -// fun `existing braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF(ALL_BRACES_SINGLE) -// -// assertThat(findings).isEmpty() -// } -// -// @TestFactory -// fun `partially missing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF(IF_BRACES_SINGLE) -// -// assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_NO_BRACE_LOCATIONS) -// } -// } + @TestFactory + fun `partially missing braces are flagged`() = listOf( + flag("if (true) { println() }"), + flag("if (true) { println() } else println()"), + flag("if (true) { println() } else if println()"), + flag("if (true) { println() } else if println() else println()"), + flag("if (true) { println() } else if println() else if println() else println()"), + ) + } @Nested inner class `=never` { - val NOTHING = arrayOf>() - private val singleLine = BracesOnIfStatements.BracePolicy.Never.config + private fun flag(code: String, vararg locations: Pair) = + testCombinations(BracePolicy.Never.config, NOT_RELEVANT, code, *locations) -// @TestFactory -// fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF(NO_BRACES_SINGLE) -// -// assertThat(findings).isEmpty() -// } -// -// @TestFactory -// fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF(ALL_BRACES_SINGLE) -// -// assertThat(findings).hasTextLocations(*ALL_BRACES_SINGLE_LOCATIONS) -// } - - context(BracesOnIfStatements) - private fun MutableList.flag(code: String, vararg locations: Pair) { - add( - dynamicTest("flags ${locations.map { TextLocation(it.first, it.second) }} in `$code`") { - val findings = compileAndLintInF(code) - - assertThat(findings).hasTextLocations(*locations.map { it.first + 13 to it.second + 13 } - .toTypedArray()) - } - ) - } + @TestFactory fun `no braces are accepted`() = listOf( + flag("if (true) println()", *NOTHING), + flag("if (true) println() else println()", *NOTHING), + flag("if (true) println() else if (true) println()", *NOTHING), + flag("if (true) println() else if (true) println() else println()", *NOTHING), + flag("if (true) println() else if println() else if (true) println() else println()", *NOTHING), + ) @TestFactory - fun `partially extra braces are flagged`() = braceTests("never", NOT_RELEVANT) { - flag("if (true) { println() }", 1 to 3) - flag("if (true) { println() } else println()", 1 to 3) - flag("if (true) { println() } else if (true) println()", 1 to 3) - flag("if (true) { println() } else if (true) println() else println()", 1 to 3) - flag( - """ - if (true) { println() } else if (true) println() else if (true) println() else println() - """.trimIndent(), - 1 to 3, - ) - } + fun `existing braces are flagged`() = listOf( + flag("if (true) { println() }"), + flag("if (true) { println() } else { println() }"), + flag("if (true) { println() } else if (true) { println() }"), + flag("if (true) { println() } else if (true) { println() } else { println() }"), + flag("if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }"), + ) - @TestFactory - fun `partially missing braces are flagged`() = braceTests("always", NOT_RELEVANT) { - flag("if (true) { println() }", *NOTHING) - flag("if (true) { println() } else println()", 25 to 29) - flag("if (true) { println() } else if (true) println()", 30 to 32) - flag("if (true) { println() } else if (true) println() else println()", 30 to 32, 50 to 52) + @TestFactory fun `partially extra braces are flagged`() = listOf( + flag("if (true) { println() }", 0 to 2), + flag("if (true) { println() } else println()", 0 to 2), + flag("if (true) { println() } else if (true) println()", 0 to 2), + flag("if (true) { println() } else if (true) println() else println()", 0 to 2), flag( """ if (true) { println() } else if (true) println() else if (true) println() else println() """.trimIndent(), - 30 to 32, 50 to 52, 75 to 79, - ) - } + 0 to 2, + ), + ) } -// + // @Nested // inner class `=necessary` { // @@ -216,21 +117,45 @@ class BracesOnIfStatementsSpec { // // @TestFactory // fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF(NO_BRACES_SINGLE) +// val findings = compileAndLintInF( +// """ +// if (true) println() +// if (true) println() else println() +// if (true) println() else if println() +// if (true) println() else if println() else println() +// if (true) println() else if println() else if println() else println() +// """.trimIndent() +// ) // // assertThat(findings).isEmpty() // } // // @TestFactory // fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF(ALL_BRACES_SINGLE) +// val findings = compileAndLintInF( +// """ +// if (true) { println() } +// if (true) { println() } else { println() } +// if (true) { println() } else if { println() } +// if (true) { println() } else if { println() } else { println() } +// if (true) { println() } else if { println() } else if { println() } else { println() } +// """.trimIndent() +// ) // // assertThat(findings).hasTextLocations(*ALL_BRACES_SINGLE_LOCATIONS) // } // // @TestFactory // fun `partially extra braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF(IF_BRACES_SINGLE) +// val findings = compileAndLintInF( +// """ +// if (true) { println() } +// if (true) { println() } else println() +// if (true) { println() } else if println() +// if (true) { println() } else if println() else println() +// if (true) { println() } else if println() else if println() else println() +// """.trimIndent() +// ) // // assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_BRACE_LOCATIONS) // } @@ -243,80 +168,135 @@ class BracesOnIfStatementsSpec { // // @TestFactory // fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF(NO_BRACES_SINGLE) +// val findings = compileAndLintInF( +// """ +// if (true) println() +// if (true) println() else println() +// if (true) println() else if println() +// if (true) println() else if println() else println() +// if (true) println() else if println() else if println() else println() +// """.trimIndent() +// ) // // assertThat(findings).isEmpty() // } // // @TestFactory // fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF(ALL_BRACES_SINGLE) +// val findings = compileAndLintInF( +// """ +// if (true) { println() } +// if (true) { println() } else { println() } +// if (true) { println() } else if { println() } +// if (true) { println() } else if { println() } else { println() } +// if (true) { println() } else if { println() } else if { println() } else { println() } +// """.trimIndent() +// ) // // assertThat(findings).isEmpty() // } // // @TestFactory // fun `partial braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF(IF_BRACES_SINGLE) +// val findings = compileAndLintInF( +// """ +// if (true) { println() } +// if (true) { println() } else println() +// if (true) { println() } else if println() +// if (true) { println() } else if println() else println() +// if (true) { println() } else if println() else if println() else println() +// """.trimIndent() +// ) // // assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_BRACE_LOCATIONS.drop(1).toTypedArray()) // } // } } -// @TestFactory -// fun `whens are not flagged`() = braceTests(NOT_RELEVANT, NOT_RELEVANT) { -// val findings = compileAndLintInF( -// """ -// when (true) { -// true -> println() -// else -> println() -// } -// """.trimIndent() -// ) -// -// assertThat(findings).isEmpty() -// } + @TestFactory + fun `whens are not flagged`() = testCombinations( + NOT_RELEVANT, + NOT_RELEVANT, + """ + when (true) { + true -> println() + else -> println() + } + """.trimIndent(), + *NOTHING + ) - private fun braceTests( + private fun testCombinations( singleLine: String, multiLine: String, - test: context(BracesOnIfStatements) MutableList.() -> Unit + code: String, + vararg locations: Pair + ): DynamicNode { + val tests = createBraceTests(singleLine, multiLine) { rule -> + // This creates a 14 character prefix (signature/9, newline/1, indent/4) for every code example. + val findings = rule.compileAndLint( + """ + fun f() { + ${code.prependIndent(" ").trimStart()} + } + """.trimIndent() + ) + // Offset text locations by the above prefix, it results in 0-indexed locations. + assertThat(findings).hasOffsetTextLocations(14, *locations) + } + val locationString = if (NOTHING.contentEquals(locations)) + "nothing" + else + locations.map { TextLocation(it.first, it.second) }.toString() + return dynamicContainer("flags $locationString in `$code`", tests) + } + + /** + * Generates a list of tests for the given brace policy combinations. + * The expectations in the test will be the same for all combinations. + * + * @see options for how the arguments are interpreted. + */ + private fun createBraceTests( + singleLine: String, + multiLine: String, + test: (BracesOnIfStatements) -> Unit ): List { val singleOptions = options(singleLine) val multiOptions = options(multiLine) - return when { - singleOptions.size > 1 && multiOptions.size > 1 -> - singleOptions.flatMap { singleLineOption -> - multiOptions.map { multiLineOption -> - val tests = mutableListOf() - test(createSubject(singleLineOption, multiLineOption), tests) - dynamicContainer("singleLine=${singleLineOption}, multiLine=${multiLineOption}", tests) + require(singleOptions.isNotEmpty() && multiOptions.isNotEmpty()) { + "No options to test: ${singleLine} -> ${singleOptions}, ${multiLine} -> ${multiOptions}" + } + return singleOptions.flatMap { singleLineOption -> + multiOptions.map { multiLineOption -> + val trace = Exception("Stack trace of TestFactory") + dynamicTest("singleLine=${singleLineOption}, multiLine=${multiLineOption}") { + try { + // Note: if you jumped here from a failed test's stack trace, + // select the "Stack trace of TestFactory" cause's last relevant line to jump to the actual test. + test(createSubject(singleLineOption, multiLineOption)) + } catch (e: Throwable) { + generateSequence(e) { e.cause }.last().initCause(trace) + throw e } } - - singleOptions.size > 1 && multiOptions.size == 1 -> - singleOptions.map { singleLineOption -> - val tests = mutableListOf() - test(createSubject(singleLineOption, multiOptions.single()), tests) - dynamicContainer("singleLine=${singleLineOption}, multiLine=${multiOptions.single()}", tests) - } - - singleOptions.size == 1 && multiOptions.size > 1 -> - multiOptions.map { multiLineOption -> - val tests = mutableListOf() - test(createSubject(singleOptions.single(), multiLineOption), tests) - dynamicContainer("singleLine=${singleOptions.single()}, multiLine=${multiLineOption}", tests) - } - - else -> - error("No options to test: $singleLine -> $singleOptions, $multiLine -> $multiOptions") + } } } + private fun createSubject(singleLine: String, multiLine: String): BracesOnIfStatements { + val config = TestConfig( + mapOf( + "singleLine" to singleLine, + "multiLine" to multiLine + ) + ) + return BracesOnIfStatements(config) + } + private fun options(option: String): List = if (option == NOT_RELEVANT) - BracesOnIfStatements.BracePolicy.values().map { it.config } + BracePolicy.values().map { it.config } else listOf(option) } diff --git a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt index cd69600c8be..12d8fcbbd5d 100644 --- a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt +++ b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt @@ -87,6 +87,23 @@ class FindingsAssert(actual: List) : } } + fun hasOffsetTextLocations(offset: Int, vararg expected: Pair) = apply { + val actualSources = actual.asSequence() + .map { it.location.text } + .map { (start, end) -> TextLocation(start - offset, end - offset) } + .sortedWith(compareBy({ it.start }, { it.end })) + + val expectedSources = expected.asSequence() + .map { (start, end) -> TextLocation(start - offset, end - offset) } + .sortedWith(compareBy({ it.start }, { it.end })) + + if (!Objects.deepEquals(actualSources.toList(), expectedSources.toList())) { + failWithMessage( + "Expected text locations to be ${expectedSources.toList()} but was ${actualSources.toList()}" + ) + } + } + fun hasTextLocations(vararg expected: String): FindingsAssert { val finding = actual.firstOrNull() if (finding == null) { diff --git a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/RuleExtensions.kt b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/RuleExtensions.kt index ef327d2d9c9..917b4ac7b48 100644 --- a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/RuleExtensions.kt +++ b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/RuleExtensions.kt @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl import java.nio.file.Path private val shouldCompileTestSnippets: Boolean = - System.getProperty("compile-test-snippets", "true")!!.toBoolean() + System.getProperty("compile-test-snippets", "false")!!.toBoolean() fun BaseRule.compileAndLint(@Language("kotlin") content: String): List { if (shouldCompileTestSnippets) { From f20db915b490a81d3c26d8775c0dbb0c73afc8a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Thu, 26 Jan 2023 16:16:48 +0000 Subject: [PATCH 10/33] Fix changing `shouldCompileTestSnippets` default not taking effect. --- build-logic/src/main/kotlin/module.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-logic/src/main/kotlin/module.gradle.kts b/build-logic/src/main/kotlin/module.gradle.kts index e0bb93a8bec..58d81a64bbf 100644 --- a/build-logic/src/main/kotlin/module.gradle.kts +++ b/build-logic/src/main/kotlin/module.gradle.kts @@ -23,8 +23,8 @@ jacoco.toolVersion = versionCatalog.findVersion("jacoco").get().requiredVersion tasks.withType().configureEach { useJUnitPlatform() systemProperty("junit.jupiter.testinstance.lifecycle.default", "per_class") - val compileTestSnippets = providers.gradleProperty("compile-test-snippets").orNull.toBoolean() - systemProperty("compile-test-snippets", compileTestSnippets) + providers.gradleProperty("compile-test-snippets").orNull + ?.let { systemProperty("compile-test-snippets", it.toBoolean()) } testLogging { // set options for log level LIFECYCLE events = setOf( From 2172b1ec2893e8859754cc44fc2636f46a17bbc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Thu, 26 Jan 2023 16:25:37 +0000 Subject: [PATCH 11/33] Fix infinite loop --- .../arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 5d19bd274b4..260149a0a07 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -276,7 +276,7 @@ class BracesOnIfStatementsSpec { // select the "Stack trace of TestFactory" cause's last relevant line to jump to the actual test. test(createSubject(singleLineOption, multiLineOption)) } catch (e: Throwable) { - generateSequence(e) { e.cause }.last().initCause(trace) + generateSequence(e) { it.cause }.last().initCause(trace) throw e } } From 1b951947e1377cfc8641df27cb299af47b3d8a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Thu, 26 Jan 2023 16:30:15 +0000 Subject: [PATCH 12/33] Restructure everything --- .../rules/style/BracesOnIfStatementsSpec.kt | 168 +++++++++--------- .../detekt/test/FindingsAssertions.kt | 18 +- 2 files changed, 90 insertions(+), 96 deletions(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 260149a0a07..f9503e8aa10 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -1,9 +1,8 @@ -@file:Suppress("ClassName", "CommentOverPrivateProperty") - package io.gitlab.arturbosch.detekt.rules.style import io.gitlab.arturbosch.detekt.api.TextLocation import io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements.BracePolicy +import io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.Companion.NOT_RELEVANT import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint @@ -13,24 +12,13 @@ import org.junit.jupiter.api.DynamicTest.dynamicTest import org.junit.jupiter.api.Nested import org.junit.jupiter.api.TestFactory -/** - * Not relevant means, that it should be covered, but for that specific test case the value doesn't matter. - * This needs to be covered still, to make sure it never becomes relevant. - * In this rule, configuration options should be separate for each config, not coupled with each other. - */ -private const val NOT_RELEVANT: String = "*" - -/** - * Nothing is expected to be flagged as a finding. - */ -private val NOTHING: Array> = emptyArray() - /** * Note: this class makes extensive use of dynamic tests and containers, few tips for maintenance: - * * Numbers are made relative to the code snippet rather than the whole code passed to Kotlin compiler. [compileAndLintInF]. + * * Numbers are made relative to the code snippet rather than the whole code passed to Kotlin compiler. [test]. * * To debug a specific test case, remove all other test cases from the same method. * * Test coverage is added for all possible configuration options, see [NOT_RELEVANT]. */ +@Suppress("ClassName") class BracesOnIfStatementsSpec { @Nested @@ -48,17 +36,17 @@ class BracesOnIfStatementsSpec { flag("if (true) println() else println()", 0 to 2), flag("if (true) println() else if (true) println()", 0 to 2), flag("if (true) println() else if (true) println() else println()", 0 to 2), - flag("if (true) println() else if println() else if (true) println() else println()", 0 to 2), + flag("if (true) println() else if (true) println() else if (true) println() else println()", 0 to 2), ) @TestFactory fun `existing braces are accepted`() = listOf( flag("if (true) { println() }", *NOTHING), flag("if (true) { println() } else { println() }", *NOTHING), - flag("if (true) { println() } else if { println() }", *NOTHING), - flag("if (true) { println() } else if { println() } else { println() }", *NOTHING), + flag("if (true) { println() } else if (true) { println() }", *NOTHING), + flag("if (true) { println() } else if (true) { println() } else { println() }", *NOTHING), flag( - "if (true) { println() } else if { println() } else if { println() } else { println() }", + "if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }", *NOTHING ), ) @@ -67,9 +55,9 @@ class BracesOnIfStatementsSpec { fun `partially missing braces are flagged`() = listOf( flag("if (true) { println() }"), flag("if (true) { println() } else println()"), - flag("if (true) { println() } else if println()"), - flag("if (true) { println() } else if println() else println()"), - flag("if (true) { println() } else if println() else if println() else println()"), + flag("if (true) { println() } else if (true) println()"), + flag("if (true) { println() } else if (true) println() else println()"), + flag("if (true) { println() } else if (true) println() else if (true) println() else println()"), ) } @@ -84,7 +72,7 @@ class BracesOnIfStatementsSpec { flag("if (true) println() else println()", *NOTHING), flag("if (true) println() else if (true) println()", *NOTHING), flag("if (true) println() else if (true) println() else println()", *NOTHING), - flag("if (true) println() else if println() else if (true) println() else println()", *NOTHING), + flag("if (true) println() else if (true) println() else if (true) println() else println()", *NOTHING), ) @TestFactory @@ -226,15 +214,55 @@ class BracesOnIfStatementsSpec { *NOTHING ) - private fun testCombinations( - singleLine: String, - multiLine: String, - code: String, - vararg locations: Pair - ): DynamicNode { - val tests = createBraceTests(singleLine, multiLine) { rule -> + companion object { + + /** + * Not relevant means, that it should be covered, but for that specific test case the value doesn't matter. + * This needs to be covered still, to make sure it never becomes relevant. + * In this rule, configuration options should be separate for each config, not coupled with each other. + */ + private const val NOT_RELEVANT: String = "*" + + /** + * Nothing is expected to be flagged as a finding. + */ + private val NOTHING: Array> = emptyArray() + + private fun createSubject(singleLine: String, multiLine: String): BracesOnIfStatements { + val config = TestConfig( + mapOf( + "singleLine" to singleLine, + "multiLine" to multiLine + ) + ) + return BracesOnIfStatements(config) + } + + private fun options(option: String): List = + if (option == NOT_RELEVANT) + BracePolicy.values().map { it.config } + else + listOf(option) + + private fun testCombinations( + singleLine: String, + multiLine: String, + code: String, + vararg locations: Pair + ): DynamicNode { + val tests = createBraceTests(singleLine, multiLine) { rule -> + rule.test(code, *locations) + } + val locationString = if (NOTHING.contentEquals(locations)) + "nothing" + else + locations.map { TextLocation(it.first, it.second) }.toString() + return dynamicContainer("flags $locationString in `$code`", tests) + } + + private fun BracesOnIfStatements.test(code: String, vararg locations: Pair) { // This creates a 14 character prefix (signature/9, newline/1, indent/4) for every code example. - val findings = rule.compileAndLint( + val findings = compileAndLint( """ fun f() { ${code.prependIndent(" ").trimStart()} @@ -244,59 +272,39 @@ class BracesOnIfStatementsSpec { // Offset text locations by the above prefix, it results in 0-indexed locations. assertThat(findings).hasOffsetTextLocations(14, *locations) } - val locationString = if (NOTHING.contentEquals(locations)) - "nothing" - else - locations.map { TextLocation(it.first, it.second) }.toString() - return dynamicContainer("flags $locationString in `$code`", tests) - } - /** - * Generates a list of tests for the given brace policy combinations. - * The expectations in the test will be the same for all combinations. - * - * @see options for how the arguments are interpreted. - */ - private fun createBraceTests( - singleLine: String, - multiLine: String, - test: (BracesOnIfStatements) -> Unit - ): List { - val singleOptions = options(singleLine) - val multiOptions = options(multiLine) - require(singleOptions.isNotEmpty() && multiOptions.isNotEmpty()) { - "No options to test: ${singleLine} -> ${singleOptions}, ${multiLine} -> ${multiOptions}" - } - return singleOptions.flatMap { singleLineOption -> - multiOptions.map { multiLineOption -> - val trace = Exception("Stack trace of TestFactory") - dynamicTest("singleLine=${singleLineOption}, multiLine=${multiLineOption}") { - try { - // Note: if you jumped here from a failed test's stack trace, - // select the "Stack trace of TestFactory" cause's last relevant line to jump to the actual test. - test(createSubject(singleLineOption, multiLineOption)) - } catch (e: Throwable) { - generateSequence(e) { it.cause }.last().initCause(trace) - throw e + /** + * Generates a list of tests for the given brace policy combinations. + * The expectations in the test will be the same for all combinations. + * + * @see options for how the arguments are interpreted. + */ + private fun createBraceTests( + singleLine: String, + multiLine: String, + test: (BracesOnIfStatements) -> Unit + ): List { + val singleOptions = options(singleLine) + val multiOptions = options(multiLine) + require(singleOptions.isNotEmpty() && multiOptions.isNotEmpty()) { + "No options to test: ${singleLine} -> ${singleOptions}, ${multiLine} -> ${multiOptions}" + } + return singleOptions.flatMap { singleLineOption -> + multiOptions.map { multiLineOption -> + val trace = Exception("Async stack trace of TestFactory") + dynamicTest("singleLine=${singleLineOption}, multiLine=${multiLineOption}") { + try { + // Note: if you jumped here from a failed test's stack trace, + // select the "Async stack trace of TestFactory" cause's + // last relevant stack line to jump to the actual test code. + test(createSubject(singleLineOption, multiLineOption)) + } catch (e: Throwable) { + generateSequence(e) { it.cause }.last().initCause(trace) + throw e + } } } } } } - - private fun createSubject(singleLine: String, multiLine: String): BracesOnIfStatements { - val config = TestConfig( - mapOf( - "singleLine" to singleLine, - "multiLine" to multiLine - ) - ) - return BracesOnIfStatements(config) - } - - private fun options(option: String): List = - if (option == NOT_RELEVANT) - BracePolicy.values().map { it.config } - else - listOf(option) } diff --git a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt index 12d8fcbbd5d..cd6d8aeb4f6 100644 --- a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt +++ b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt @@ -71,21 +71,7 @@ class FindingsAssert(actual: List) : hasEndSourceLocations(SourceLocation(line, column)) } - fun hasTextLocations(vararg expected: Pair) = apply { - val actualSources = actual.asSequence() - .map { it.location.text } - .sortedWith(compareBy({ it.start }, { it.end })) - - val expectedSources = expected.asSequence() - .map { (start, end) -> TextLocation(start, end) } - .sortedWith(compareBy({ it.start }, { it.end })) - - if (!Objects.deepEquals(actualSources.toList(), expectedSources.toList())) { - failWithMessage( - "Expected text locations to be ${expectedSources.toList()} but was ${actualSources.toList()}" - ) - } - } + fun hasTextLocations(vararg expected: Pair) = hasOffsetTextLocations(0, *expected) fun hasOffsetTextLocations(offset: Int, vararg expected: Pair) = apply { val actualSources = actual.asSequence() @@ -94,7 +80,7 @@ class FindingsAssert(actual: List) : .sortedWith(compareBy({ it.start }, { it.end })) val expectedSources = expected.asSequence() - .map { (start, end) -> TextLocation(start - offset, end - offset) } + .map { (start, end) -> TextLocation(start, end) } .sortedWith(compareBy({ it.start }, { it.end })) if (!Objects.deepEquals(actualSources.toList(), expectedSources.toList())) { From 77c8d7fe3b8bf87672e5f41ea54ee93637f315d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Thu, 26 Jan 2023 16:54:05 +0000 Subject: [PATCH 13/33] Fill in some numbers --- .../rules/style/BracesOnIfStatementsSpec.kt | 243 +++++++++--------- 1 file changed, 128 insertions(+), 115 deletions(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index f9503e8aa10..00b58c1943b 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -3,6 +3,7 @@ package io.gitlab.arturbosch.detekt.rules.style import io.gitlab.arturbosch.detekt.api.TextLocation import io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatements.BracePolicy import io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.Companion.NOT_RELEVANT +import io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.Companion.test import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint @@ -33,10 +34,23 @@ class BracesOnIfStatementsSpec { @TestFactory fun `missing braces are flagged`() = listOf( flag("if (true) println()", 0 to 2), - flag("if (true) println() else println()", 0 to 2), - flag("if (true) println() else if (true) println()", 0 to 2), - flag("if (true) println() else if (true) println() else println()", 0 to 2), - flag("if (true) println() else if (true) println() else if (true) println() else println()", 0 to 2), + flag("if (true) println() else println()", 0 to 2, 20 to 24), + flag( + "if (true) println() else if (true) println()", + 0 to 2, 25 to 27 + ), + flag( + "if (true) println() else if (true) println() else println()", + 0 to 2, 25 to 27, 45 to 49 + ), + flag( + "if (true) println() else if (true) println() else if (true) println()", + 0 to 2, 25 to 27, 50 to 52 + ), + flag( + "if (true) println() else if (true) println() else if (true) println() else println()", + 0 to 2, 25 to 27, 50 to 52, 70 to 74 + ), ) @TestFactory @@ -45,6 +59,7 @@ class BracesOnIfStatementsSpec { flag("if (true) { println() } else { println() }", *NOTHING), flag("if (true) { println() } else if (true) { println() }", *NOTHING), flag("if (true) { println() } else if (true) { println() } else { println() }", *NOTHING), + flag("if (true) { println() } else if (true) { println() } else if (true) { println() }", *NOTHING), flag( "if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }", *NOTHING @@ -57,6 +72,7 @@ class BracesOnIfStatementsSpec { flag("if (true) { println() } else println()"), flag("if (true) { println() } else if (true) println()"), flag("if (true) { println() } else if (true) println() else println()"), + flag("if (true) { println() } else if (true) println() else if (true) println()"), flag("if (true) { println() } else if (true) println() else if (true) println() else println()"), ) } @@ -72,16 +88,18 @@ class BracesOnIfStatementsSpec { flag("if (true) println() else println()", *NOTHING), flag("if (true) println() else if (true) println()", *NOTHING), flag("if (true) println() else if (true) println() else println()", *NOTHING), + flag("if (true) println() else if (true) println() else if (true) println()", *NOTHING), flag("if (true) println() else if (true) println() else if (true) println() else println()", *NOTHING), ) @TestFactory fun `existing braces are flagged`() = listOf( - flag("if (true) { println() }"), - flag("if (true) { println() } else { println() }"), - flag("if (true) { println() } else if (true) { println() }"), - flag("if (true) { println() } else if (true) { println() } else { println() }"), - flag("if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }"), + flag("if (true) { println() }" /*TODO*/), + flag("if (true) { println() } else { println() }" /*TODO*/), + flag("if (true) { println() } else if (true) { println() }" /*TODO*/), + flag("if (true) { println() } else if (true) { println() } else { println() }" /*TODO*/), + flag("if (true) { println() } else if (true) { println() } else if (true) { println() }" /*TODO*/), + flag("if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }" /*TODO*/), ) @TestFactory fun `partially extra braces are flagged`() = listOf( @@ -89,116 +107,111 @@ class BracesOnIfStatementsSpec { flag("if (true) { println() } else println()", 0 to 2), flag("if (true) { println() } else if (true) println()", 0 to 2), flag("if (true) { println() } else if (true) println() else println()", 0 to 2), + flag("if (true) { println() } else if (true) println() else if (true) println()", 0 to 2), + flag( + "if (true) { println() } else if (true) println() else if (true) println() else println()", + 0 to 2 + ), + // TODO add more cases, not just first branch + ) + + @TestFactory fun `fully extra braces are flagged`() = listOf( + flag("if (true) { println() }", 0 to 2), + flag("if (true) { println() } else { println() }", 0 to 2), + flag("if (true) { println() } else if (true) { println() }", 0 to 2), + flag("if (true) { println() } else if (true) { println() } else { println() }", 0 to 2), + flag("if (true) { println() } else if (true) { println() } else if (true) { println() }", 0 to 2), flag( - """ - if (true) { println() } else if (true) println() else if (true) println() else println() - """.trimIndent(), - 0 to 2, + "if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }", + 0 to 2 ), ) } -// @Nested -// inner class `=necessary` { -// -// private val singleLine = BracesOnIfStatements.BracePolicy.Necessary.config -// -// @TestFactory -// fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF( -// """ -// if (true) println() -// if (true) println() else println() -// if (true) println() else if println() -// if (true) println() else if println() else println() -// if (true) println() else if println() else if println() else println() -// """.trimIndent() -// ) -// -// assertThat(findings).isEmpty() -// } -// -// @TestFactory -// fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF( -// """ -// if (true) { println() } -// if (true) { println() } else { println() } -// if (true) { println() } else if { println() } -// if (true) { println() } else if { println() } else { println() } -// if (true) { println() } else if { println() } else if { println() } else { println() } -// """.trimIndent() -// ) -// -// assertThat(findings).hasTextLocations(*ALL_BRACES_SINGLE_LOCATIONS) -// } -// -// @TestFactory -// fun `partially extra braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF( -// """ -// if (true) { println() } -// if (true) { println() } else println() -// if (true) { println() } else if println() -// if (true) { println() } else if println() else println() -// if (true) { println() } else if println() else if println() else println() -// """.trimIndent() -// ) -// -// assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_BRACE_LOCATIONS) -// } -// } -// -// @Nested -// inner class `=consistent` { -// -// private val singleLine = BracesOnIfStatements.BracePolicy.Consistent.config -// -// @TestFactory -// fun `no braces are accepted`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF( -// """ -// if (true) println() -// if (true) println() else println() -// if (true) println() else if println() -// if (true) println() else if println() else println() -// if (true) println() else if println() else if println() else println() -// """.trimIndent() -// ) -// -// assertThat(findings).isEmpty() -// } -// -// @TestFactory -// fun `existing braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF( -// """ -// if (true) { println() } -// if (true) { println() } else { println() } -// if (true) { println() } else if { println() } -// if (true) { println() } else if { println() } else { println() } -// if (true) { println() } else if { println() } else if { println() } else { println() } -// """.trimIndent() -// ) -// -// assertThat(findings).isEmpty() -// } -// -// @TestFactory -// fun `partial braces are flagged`() = braceTests(singleLine, NOT_RELEVANT) { -// val findings = compileAndLintInF( -// """ -// if (true) { println() } -// if (true) { println() } else println() -// if (true) { println() } else if println() -// if (true) { println() } else if println() else println() -// if (true) { println() } else if println() else if println() else println() -// """.trimIndent() -// ) -// -// assertThat(findings).hasTextLocations(*IF_BRACES_SINGLE_BRACE_LOCATIONS.drop(1).toTypedArray()) -// } -// } + @Nested + inner class `=necessary` { + + private fun flag(code: String, vararg locations: Pair) = + testCombinations(BracePolicy.Necessary.config, NOT_RELEVANT, code, *locations) + + @TestFactory + fun `no braces are accepted`() = listOf( + flag("if (true) println()", *NOTHING), + flag("if (true) println() else println()", *NOTHING), + flag("if (true) println() else if println()", *NOTHING), + flag("if (true) println() else if println() else println()", *NOTHING), + flag("if (true) println() else if println() else if println()", *NOTHING), + flag("if (true) println() else if println() else if println() else println()", *NOTHING), + ) + + @TestFactory + fun `existing braces are flagged`() = listOf( + flag("if (true) { println() }", 0 to 2), + flag("if (true) { println() } else { println() }", 0 to 2 /*TODO*/), + flag("if (true) { println() } else if { println() }", 0 to 2 /*TODO*/), + flag("if (true) { println() } else if { println() } else { println() }", 0 to 2 /*TODO*/), + flag("if (true) { println() } else if { println() } else if { println() }", 0 to 2 /*TODO*/), + flag( + "if (true) { println() } else if { println() } else if { println() } else { println() }", + 0 to 2, /*TODO*/ + ), + ) + + @TestFactory + fun `partially extra braces are flagged`() = listOf( + flag("if (true) { println() }", 0 to 2), + flag("if (true) { println() } else println()", 0 to 2 /*TODO*/), + flag("if (true) { println() } else if println()", 0 to 2 /*TODO*/), + flag("if (true) { println() } else if println() else println()", 0 to 2 /*TODO*/), + flag("if (true) { println() } else if println() else if println()", 0 to 2 /*TODO*/), + flag( + "if (true) { println() } else if println() else if println() else println()", + 0 to 2 /*TODO*/ + ), + // TODO add more cases, not just first branch + ) + } + + @Nested + inner class `=consistent` { + + private fun flag(code: String, vararg locations: Pair) = + testCombinations(BracePolicy.Consistent.config, NOT_RELEVANT, code, *locations) + + @TestFactory + fun `no braces are accepted`() = listOf( + flag("if (true) println()", *NOTHING), + flag("if (true) println() else println()", *NOTHING), + flag("if (true) println() else if println()", *NOTHING), + flag("if (true) println() else if println() else println()", *NOTHING), + flag("if (true) println() else if println() else if println()", *NOTHING), + flag("if (true) println() else if println() else if println() else println()", *NOTHING), + ) + + @TestFactory + fun `existing braces are flagged`() = listOf( + flag("if (true) { println() }", *NOTHING), + flag("if (true) { println() } else { println() }", *NOTHING), + flag("if (true) { println() } else if { println() }", *NOTHING), + flag("if (true) { println() } else if { println() } else { println() }", *NOTHING), + flag("if (true) { println() } else if { println() } else if { println() }", *NOTHING), + flag( + "if (true) { println() } else if { println() } else if { println() } else { println() }", + *NOTHING + ), + ) + + @TestFactory + fun `partial braces are flagged`() = listOf( + //flag("if (true) { println() }", *NOTHING), + flag("if (true) { println() } else println()", 0 to 2), + flag("if (true) { println() } else if println()", 0 to 2), + flag("if (true) { println() } else if println() else println()", 0 to 2), + flag("if (true) { println() } else if println() else if println()", 0 to 2), + flag("if (true) { println() } else if println() else if println() else println()", 0 to 2), + // TODO add more cases, not just first branch + ) + } } @TestFactory @@ -237,7 +250,7 @@ class BracesOnIfStatementsSpec { ) return BracesOnIfStatements(config) } - + private fun options(option: String): List = if (option == NOT_RELEVANT) BracePolicy.values().map { it.config } From e6b16db3a168aa583cf9314a76734f8ca35ecea9 Mon Sep 17 00:00:00 2001 From: "Vitaly V. Pinchuk" Date: Sun, 29 Jan 2023 14:48:47 -0400 Subject: [PATCH 14/33] Fix after merge --- build-logic/src/main/kotlin/module.gradle.kts | 4 +- .../rules/style/BracesOnIfStatementsSpec.kt | 1345 +++++++++++++++-- .../MandatoryBracesIfStatementsSpec.kt | 1 - .../detekt/test/FindingsAssertions.kt | 5 +- 4 files changed, 1256 insertions(+), 99 deletions(-) diff --git a/build-logic/src/main/kotlin/module.gradle.kts b/build-logic/src/main/kotlin/module.gradle.kts index 58d81a64bbf..e0bb93a8bec 100644 --- a/build-logic/src/main/kotlin/module.gradle.kts +++ b/build-logic/src/main/kotlin/module.gradle.kts @@ -23,8 +23,8 @@ jacoco.toolVersion = versionCatalog.findVersion("jacoco").get().requiredVersion tasks.withType().configureEach { useJUnitPlatform() systemProperty("junit.jupiter.testinstance.lifecycle.default", "per_class") - providers.gradleProperty("compile-test-snippets").orNull - ?.let { systemProperty("compile-test-snippets", it.toBoolean()) } + val compileTestSnippets = providers.gradleProperty("compile-test-snippets").orNull.toBoolean() + systemProperty("compile-test-snippets", compileTestSnippets) testLogging { // set options for log level LIFECYCLE events = setOf( diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 00b58c1943b..7f0b85f583d 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -1,3 +1,5 @@ +@file:Suppress("ForbiddenImport") + package io.gitlab.arturbosch.detekt.rules.style import io.gitlab.arturbosch.detekt.api.TextLocation @@ -7,10 +9,12 @@ import io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.Companio import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.DynamicContainer.dynamicContainer import org.junit.jupiter.api.DynamicNode import org.junit.jupiter.api.DynamicTest.dynamicTest import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestFactory /** @@ -19,37 +23,66 @@ import org.junit.jupiter.api.TestFactory * * To debug a specific test case, remove all other test cases from the same method. * * Test coverage is added for all possible configuration options, see [NOT_RELEVANT]. */ -@Suppress("ClassName") +@Suppress( + "ClassName", + "LongMethod", + "CommentOverPrivateProperty" +) class BracesOnIfStatementsSpec { + @Test + fun `validate behavior of occurrence function`() { + val code = "fun f() { if (true) else if (true) if (true) true }" + Assertions.assertEquals(10 to 12, "if"(1)(code)) + Assertions.assertEquals(25 to 27, "if"(2)(code)) + Assertions.assertEquals(35 to 37, "if"(3)(code)) + Assertions.assertEquals(20 to 24, "else"(1)(code)) + Assertions.assertThrows(IllegalArgumentException::class.java) { "if"(4)(code) } + Assertions.assertThrows(IllegalArgumentException::class.java) { "if"(0)(code) } + Assertions.assertThrows(IllegalArgumentException::class.java) { "else"(2)(code) } + } + @Nested inner class singleLine { @Nested inner class `=always` { - private fun flag(code: String, vararg locations: Pair) = - testCombinations(BracePolicy.Always.config, NOT_RELEVANT, code, *locations) + private fun flag(code: String, vararg locations: (String) -> Pair) = + testCombinations( + BracePolicy.Always.config, + NOT_RELEVANT, + code, + *(locations.map { it(code) }.toTypedArray()) + ) @TestFactory fun `missing braces are flagged`() = listOf( - flag("if (true) println()", 0 to 2), - flag("if (true) println() else println()", 0 to 2, 20 to 24), + flag("if (true) println()", "if"(1)), + flag("if (true) println() else println()", "if"(1), "else"(1)), flag( "if (true) println() else if (true) println()", - 0 to 2, 25 to 27 + "if"(1), + "if"(2) ), flag( "if (true) println() else if (true) println() else println()", - 0 to 2, 25 to 27, 45 to 49 + "if"(1), + "if"(2), + "else"(2) ), flag( "if (true) println() else if (true) println() else if (true) println()", - 0 to 2, 25 to 27, 50 to 52 + "if"(1), + "if"(2), + "if"(3) ), flag( "if (true) println() else if (true) println() else if (true) println() else println()", - 0 to 2, 25 to 27, 50 to 52, 70 to 74 + "if"(1), + "if"(2), + "if"(3), + "else"(3) ), ) @@ -59,7 +92,10 @@ class BracesOnIfStatementsSpec { flag("if (true) { println() } else { println() }", *NOTHING), flag("if (true) { println() } else if (true) { println() }", *NOTHING), flag("if (true) { println() } else if (true) { println() } else { println() }", *NOTHING), - flag("if (true) { println() } else if (true) { println() } else if (true) { println() }", *NOTHING), + flag( + "if (true) { println() } else if (true) { println() } else if (true) { println() }", + *NOTHING + ), flag( "if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }", *NOTHING @@ -68,20 +104,36 @@ class BracesOnIfStatementsSpec { @TestFactory fun `partially missing braces are flagged`() = listOf( - flag("if (true) { println() }"), - flag("if (true) { println() } else println()"), - flag("if (true) { println() } else if (true) println()"), - flag("if (true) { println() } else if (true) println() else println()"), - flag("if (true) { println() } else if (true) println() else if (true) println()"), - flag("if (true) { println() } else if (true) println() else if (true) println() else println()"), + flag("if (true) { println() } else println()", "else"(1)), + flag("if (true) { println() } else if (true) println()", "if"(2)), + flag( + "if (true) { println() } else if (true) println() else println()", + "if"(2), + "else"(2) + ), + flag( + "if (true) { println() } else if (true) println() else if (true) println()", + "if"(2), + "if"(3) + ), + flag( + "if (true) { println() } else if (true) println() else if (true) println() else println()", + "if"(2), + "if"(3), + "else"(3) + ), ) } @Nested inner class `=never` { - private fun flag(code: String, vararg locations: Pair) = - testCombinations(BracePolicy.Never.config, NOT_RELEVANT, code, *locations) + private fun flag(code: String, vararg locations: (String) -> Pair) = testCombinations( + BracePolicy.Never.config, + NOT_RELEVANT, + code, + *(locations.map { it(code) }.toTypedArray()) + ) @TestFactory fun `no braces are accepted`() = listOf( flag("if (true) println()", *NOTHING), @@ -89,41 +141,82 @@ class BracesOnIfStatementsSpec { flag("if (true) println() else if (true) println()", *NOTHING), flag("if (true) println() else if (true) println() else println()", *NOTHING), flag("if (true) println() else if (true) println() else if (true) println()", *NOTHING), - flag("if (true) println() else if (true) println() else if (true) println() else println()", *NOTHING), + flag( + "if (true) println() else if (true) println() else if (true) println() else println()", + *NOTHING + ), ) @TestFactory fun `existing braces are flagged`() = listOf( - flag("if (true) { println() }" /*TODO*/), - flag("if (true) { println() } else { println() }" /*TODO*/), - flag("if (true) { println() } else if (true) { println() }" /*TODO*/), - flag("if (true) { println() } else if (true) { println() } else { println() }" /*TODO*/), - flag("if (true) { println() } else if (true) { println() } else if (true) { println() }" /*TODO*/), - flag("if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }" /*TODO*/), + flag("if (true) { println() }", "if"(1)), + flag("if (true) { println() } else { println() }", "if"(1), "else"(1)), + flag("if (true) { println() } else if (true) { println() }", "if"(1), "if"(2)), + flag( + "if (true) { println() } else if (true) { println() } else { println() }", + "if"(1), + "if"(2), + "else"(2) + ), + flag( + "if (true) { println() } else if (true) { println() } else if (true) { println() }", + "if"(1), + "if"(2), + "if"(3) + ), + flag( + "if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }", + "if"(1), + "if"(2), + "if"(3), + "else"(3) + ), ) - @TestFactory fun `partially extra braces are flagged`() = listOf( - flag("if (true) { println() }", 0 to 2), - flag("if (true) { println() } else println()", 0 to 2), - flag("if (true) { println() } else if (true) println()", 0 to 2), - flag("if (true) { println() } else if (true) println() else println()", 0 to 2), - flag("if (true) { println() } else if (true) println() else if (true) println()", 0 to 2), + @TestFactory + fun `partially extra braces are flagged`() = listOf( + flag("if (true) { println() }", "if"(1)), + flag("if (true) { println() } else println()", "if"(1)), + flag("if (true) { println() } else if (true) println()", "if"(1)), + flag("if (true) { println() } else if (true) println() else println()", "if"(1)), + flag("if (true) { println() } else if (true) println() else if (true) println()", "if"(1)), flag( "if (true) { println() } else if (true) println() else if (true) println() else println()", - 0 to 2 + "if"(1) + ), + // More cases, not just first branch + flag("if (true) println() else { println() }", "else"(1)), + flag("if (true) println() else if (true) { println() }", "if"(2)), + flag("if (true) println() else if (true) println() else { println() }", "else"(2)), + flag("if (true) println() else if (true) println() else if (true) { println() }", "if"(3)), + flag( + "if (true) println() else if (true) println() else if (true) println() else { println() }", + "else"(3) ), - // TODO add more cases, not just first branch ) @TestFactory fun `fully extra braces are flagged`() = listOf( - flag("if (true) { println() }", 0 to 2), - flag("if (true) { println() } else { println() }", 0 to 2), - flag("if (true) { println() } else if (true) { println() }", 0 to 2), - flag("if (true) { println() } else if (true) { println() } else { println() }", 0 to 2), - flag("if (true) { println() } else if (true) { println() } else if (true) { println() }", 0 to 2), + flag("if (true) { println() }", "if"(1)), + flag("if (true) { println() } else { println() }", "if"(1), "else"(1)), + flag("if (true) { println() } else if (true) { println() }", "if"(1), "if"(2)), + flag( + "if (true) { println() } else if (true) { println() } else { println() }", + "if"(1), + "if"(2), + "else"(2) + ), + flag( + "if (true) { println() } else if (true) { println() } else if (true) { println() }", + "if"(1), + "if"(2), + "if"(3) + ), flag( "if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }", - 0 to 2 + "if"(1), + "if"(2), + "if"(3), + "else"(3) ), ) } @@ -131,42 +224,64 @@ class BracesOnIfStatementsSpec { @Nested inner class `=necessary` { - private fun flag(code: String, vararg locations: Pair) = - testCombinations(BracePolicy.Necessary.config, NOT_RELEVANT, code, *locations) + private fun flag(code: String, vararg locations: (String) -> Pair) = + testCombinations( + BracePolicy.Necessary.config, + NOT_RELEVANT, + code, + *(locations.map { it(code) }.toTypedArray()) + ) @TestFactory fun `no braces are accepted`() = listOf( flag("if (true) println()", *NOTHING), flag("if (true) println() else println()", *NOTHING), - flag("if (true) println() else if println()", *NOTHING), - flag("if (true) println() else if println() else println()", *NOTHING), - flag("if (true) println() else if println() else if println()", *NOTHING), - flag("if (true) println() else if println() else if println() else println()", *NOTHING), + flag("if (true) println() else if (true) println()", *NOTHING), + flag("if (true) println() else if (true) println() else println()", *NOTHING), + flag("if (true) println() else if (true) println() else if (true) println()", *NOTHING), + flag("if (true) println() else if (true) println() else if (true) println() else println()", *NOTHING), ) @TestFactory fun `existing braces are flagged`() = listOf( - flag("if (true) { println() }", 0 to 2), - flag("if (true) { println() } else { println() }", 0 to 2 /*TODO*/), - flag("if (true) { println() } else if { println() }", 0 to 2 /*TODO*/), - flag("if (true) { println() } else if { println() } else { println() }", 0 to 2 /*TODO*/), - flag("if (true) { println() } else if { println() } else if { println() }", 0 to 2 /*TODO*/), + flag("if (true) { println() }", "if"(1)), + flag("if (true) { println() } else { println() }", "if"(1), "else"(1)), + flag( + "if (true) { println() } else if (true) { println() }", + "if"(1), + "if"(2) + ), + flag( + "if (true) { println() } else if (true) { println() } else { println() }", + "if"(1), + "if"(2), + "else"(2) + ), flag( - "if (true) { println() } else if { println() } else if { println() } else { println() }", - 0 to 2, /*TODO*/ + "if (true) { println() } else if (true) { println() } else if (true) { println() }", + "if"(1), + "if"(2), + "if"(3) + ), + flag( + "if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }", + "if"(1), + "if"(2), + "if"(3), + "else"(3) ), ) @TestFactory fun `partially extra braces are flagged`() = listOf( - flag("if (true) { println() }", 0 to 2), - flag("if (true) { println() } else println()", 0 to 2 /*TODO*/), - flag("if (true) { println() } else if println()", 0 to 2 /*TODO*/), - flag("if (true) { println() } else if println() else println()", 0 to 2 /*TODO*/), - flag("if (true) { println() } else if println() else if println()", 0 to 2 /*TODO*/), + flag("if (true) { println() }", "if"(1)), + flag("if (true) { println() } else println()", "if"(1)), + flag("if (true) { println() } else if (true) println()", "if"(1)), + flag("if (true) { println() } else if (true) println() else println()", "if"(1)), + flag("if (true) { println() } else if (true) println() else if (true) println()", "if"(1)), flag( - "if (true) { println() } else if println() else if println() else println()", - 0 to 2 /*TODO*/ + "if (true) { println() } else if (true) println() else if (true) println() else println()", + "if"(1) ), // TODO add more cases, not just first branch ) @@ -175,43 +290,1076 @@ class BracesOnIfStatementsSpec { @Nested inner class `=consistent` { - private fun flag(code: String, vararg locations: Pair) = - testCombinations(BracePolicy.Consistent.config, NOT_RELEVANT, code, *locations) + private fun flag(code: String, vararg locations: (String) -> Pair) = + testCombinations( + BracePolicy.Consistent.config, + NOT_RELEVANT, + code, + *(locations.map { it(code) }.toTypedArray()) + ) @TestFactory fun `no braces are accepted`() = listOf( flag("if (true) println()", *NOTHING), flag("if (true) println() else println()", *NOTHING), - flag("if (true) println() else if println()", *NOTHING), - flag("if (true) println() else if println() else println()", *NOTHING), - flag("if (true) println() else if println() else if println()", *NOTHING), - flag("if (true) println() else if println() else if println() else println()", *NOTHING), + flag("if (true) println() else if (true) println()", *NOTHING), + flag("if (true) println() else if (true) println() else println()", *NOTHING), + flag("if (true) println() else if (true) println() else if (true) println()", *NOTHING), + flag("if (true) println() else if (true) println() else if (true) println() else println()", *NOTHING), ) @TestFactory fun `existing braces are flagged`() = listOf( flag("if (true) { println() }", *NOTHING), flag("if (true) { println() } else { println() }", *NOTHING), - flag("if (true) { println() } else if { println() }", *NOTHING), - flag("if (true) { println() } else if { println() } else { println() }", *NOTHING), - flag("if (true) { println() } else if { println() } else if { println() }", *NOTHING), + flag("if (true) { println() } else if (true) { println() }", *NOTHING), + flag("if (true) { println() } else if (true) { println() } else { println() }", *NOTHING), + flag("if (true) { println() } else if (true) { println() } else if (true) { println() }", *NOTHING), flag( - "if (true) { println() } else if { println() } else if { println() } else { println() }", + "if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }", *NOTHING ), ) @TestFactory fun `partial braces are flagged`() = listOf( - //flag("if (true) { println() }", *NOTHING), - flag("if (true) { println() } else println()", 0 to 2), - flag("if (true) { println() } else if println()", 0 to 2), - flag("if (true) { println() } else if println() else println()", 0 to 2), - flag("if (true) { println() } else if println() else if println()", 0 to 2), - flag("if (true) { println() } else if println() else if println() else println()", 0 to 2), + flag("if (true) { println() }", *NOTHING), + flag("if (true) { println() } else println()", "if"(1)), + flag("if (true) { println() } else if (true) println()", "if"(1)), + flag("if (true) { println() } else if (true) println() else println()", "if"(1)), + flag("if (true) { println() } else if (true) println() else if (true) println()", "if"(1)), + flag( + "if (true) { println() } else if (true) println() else if (true) println() else println()", + "if"(1) + ), + // More cases, not just first branch + flag("if (true) println() else { println() }", "if"(1)), + flag( + "if (true) { println() } else if (true) println() else if (true) println() else { println() }", + "if"(1) + ), + ) + } + } + + @Nested + inner class multiLine { + + @Nested + inner class `=always` { + + private fun flag(code: String, vararg locations: (String) -> Pair) = + testCombinations( + NOT_RELEVANT, + BracePolicy.Always.config, + code, + *(locations.map { it(code) }.toTypedArray()) + ) + + @TestFactory + fun `missing braces are flagged`() = listOf( + flag( + """ + if (true) + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else + println() + """.trimIndent(), + "if"(1), + "else"(1) + ), + flag( + """ + if (true) + println() + else if (true) + println() + """.trimIndent(), + "if"(1), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else + println() + """.trimIndent(), + "if"(1), + "if"(2), + "else"(2) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + """.trimIndent(), + "if"(1), + "if"(2), + "if"(3) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + else println() + """.trimIndent(), + "if"(1), + "if"(2), + "if"(3), + "else"(3) + ), + ) + + @TestFactory + fun `existing braces are accepted`() = listOf( + flag( + """ + if (true) { + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) { + println() + } else { + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else { + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else if (true) { + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else if (true) { + println() + } else { + println() + } + """.trimIndent(), + *NOTHING + ), + ) + + @TestFactory + fun `partially missing braces are flagged`() = listOf( + flag( + """ + if (true) { + println() + } else + println() + """.trimIndent(), + "else"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else + println() + """.trimIndent(), + "if"(2), + "else"(2) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else if (true) + println() + """.trimIndent(), + "if"(2), + "if"(3) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else if (true) + println() + else + println() + """.trimIndent(), + "if"(2), + "if"(3), + "else"(3) + ), + ) + } + + @Nested + inner class `=never` { + private fun flag(code: String, vararg locations: (String) -> Pair) = testCombinations( + NOT_RELEVANT, + BracePolicy.Never.config, + code, + *(locations.map { it(code) }.toTypedArray()) + ) + + @TestFactory fun `no braces are accepted`() = listOf( + flag( + """ + if (true) + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + else + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + else + println() + """.trimIndent(), + *NOTHING + ), + ) + + @TestFactory + fun `existing braces are flagged`() = listOf( + flag( + """ + if (true) { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else { + println() + } + """.trimIndent(), + "if"(1), + "else"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } + """.trimIndent(), + "if"(1), + "if"(2) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else { + println() + } + """.trimIndent(), + "if"(1), + "if"(2), + "else"(2) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else if (true) { + println() + } + """.trimIndent(), + "if"(1), + "if"(2), + "if"(3) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else if (true) { + println() + } else { + println() + } + """.trimIndent(), + "if"(1), + "if"(2), + "if"(3), + "else"(3) + ), + ) + + @TestFactory + fun `partially extra braces are flagged`() = listOf( + flag( + """ + if (true) { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else if (true) + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else if (true) + println() + else + println() + """.trimIndent(), + "if"(1) + ), + // More cases, not just first branch + flag( + """ + if (true) + println() + else { + println() + } + """.trimIndent(), + "else"(1) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "else"(2) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(3) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "else"(3) + ), + ) + + @TestFactory fun `fully extra braces are flagged`() = listOf( + flag( + """ + if (true) { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else { + println() + } + """.trimIndent(), + "if"(1), + "else"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } + """.trimIndent(), + "if"(1), + "if"(2) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else { + println() + } + """.trimIndent(), + "if"(1), + "if"(2), + "else"(2) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else if (true) { + println() + } + """.trimIndent(), + "if"(1), + "if"(2), + "if"(3) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else if (true) { + println() + } else { + println() + } + """.trimIndent(), + "if"(1), + "if"(2), + "if"(3), + "else"(3) + ), + ) + } + + @Nested + inner class `=necessary` { + + private fun flag(code: String, vararg locations: (String) -> Pair) = + testCombinations( + NOT_RELEVANT, + BracePolicy.Necessary.config, + code, + *(locations.map { it(code) }.toTypedArray()) + ) + + @TestFactory + fun `no braces are accepted`() = listOf( + flag( + """ + if (true) + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + else + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + else + println() + """.trimIndent(), + *NOTHING + ), + ) + + @TestFactory + fun `existing braces are flagged`() = listOf( + flag( + """ + if (true) { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else { + println() + } + """.trimIndent(), + "if"(1), + "else"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } + """.trimIndent(), + "if"(1), + "if"(2) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else { + println() + } + """.trimIndent(), + "if"(1), + "if"(2), + "else"(2) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else if (true) { + println() + } + """.trimIndent(), + "if"(1), + "if"(2), + "if"(3) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else if (true) { + println() + } else { + println() + } + """.trimIndent(), + "if"(1), + "if"(2), + "if"(3), + "else"(3) + ), + ) + + @TestFactory + fun `partially extra braces are flagged`() = listOf( + flag( + """ + if (true) { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else if (true) + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else if (true) + println() + else + println() + """.trimIndent(), + "if"(1) + ), // TODO add more cases, not just first branch ) } + + @Nested + inner class `=consistent` { + + private fun flag(code: String, vararg locations: (String) -> Pair) = + testCombinations( + NOT_RELEVANT, + BracePolicy.Consistent.config, + code, + *(locations.map { it(code) }.toTypedArray()) + ) + + @TestFactory + fun `no braces are accepted`() = listOf( + flag( + """ + if (true) + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + else + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + else + println() + """.trimIndent(), + *NOTHING + ), + ) + + @TestFactory + fun `existing braces are flagged`() = listOf( + flag( + """ + if (true) { + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) { + println() + } else { + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else { + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else if (true) { + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else if (true) { + println() + } else { + println() + } + """.trimIndent(), + *NOTHING + ), + ) + + @TestFactory + fun `partial braces are flagged`() = listOf( + flag( + """ + if (true) { + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) { + println() + } else + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else if (true) + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else if (true) + println() + else + println() + """.trimIndent(), + "if"(1) + ), + // More cases, not just first branch + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "if"(1) + ), + ) + } } @TestFactory @@ -224,7 +1372,7 @@ class BracesOnIfStatementsSpec { else -> println() } """.trimIndent(), - *NOTHING + *(NOTHING.map { it("") }.toTypedArray()) ) companion object { @@ -239,7 +1387,7 @@ class BracesOnIfStatementsSpec { /** * Nothing is expected to be flagged as a finding. */ - private val NOTHING: Array> = emptyArray() + private val NOTHING: Array<(String) -> Pair> = emptyArray() private fun createSubject(singleLine: String, multiLine: String): BracesOnIfStatements { val config = TestConfig( @@ -252,10 +1400,11 @@ class BracesOnIfStatementsSpec { } private fun options(option: String): List = - if (option == NOT_RELEVANT) + if (option == NOT_RELEVANT) { BracePolicy.values().map { it.config } - else + } else { listOf(option) + } private fun testCombinations( singleLine: String, @@ -266,24 +1415,23 @@ class BracesOnIfStatementsSpec { val tests = createBraceTests(singleLine, multiLine) { rule -> rule.test(code, *locations) } - val locationString = if (NOTHING.contentEquals(locations)) + val locationString = if (NOTHING.contentEquals(locations)) { "nothing" - else + } else { locations.map { TextLocation(it.first, it.second) }.toString() + } return dynamicContainer("flags $locationString in `$code`", tests) } private fun BracesOnIfStatements.test(code: String, vararg locations: Pair) { - // This creates a 14 character prefix (signature/9, newline/1, indent/4) for every code example. - val findings = compileAndLint( - """ - fun f() { - ${code.prependIndent(" ").trimStart()} - } - """.trimIndent() - ) + // This creates a 10 character prefix (signature/9, space/1) for every code example. + val findings = compileAndLint("fun f() { $code }") // Offset text locations by the above prefix, it results in 0-indexed locations. - assertThat(findings).hasOffsetTextLocations(14, *locations) + val offset = 10 + assertThat(findings) + .hasTextLocations( + *(locations.map { it.first + offset to it.second + offset }).toTypedArray() + ) } /** @@ -300,12 +1448,12 @@ class BracesOnIfStatementsSpec { val singleOptions = options(singleLine) val multiOptions = options(multiLine) require(singleOptions.isNotEmpty() && multiOptions.isNotEmpty()) { - "No options to test: ${singleLine} -> ${singleOptions}, ${multiLine} -> ${multiOptions}" + "No options to test: $singleLine -> $singleOptions, $multiLine -> $multiOptions" } return singleOptions.flatMap { singleLineOption -> multiOptions.map { multiLineOption -> val trace = Exception("Async stack trace of TestFactory") - dynamicTest("singleLine=${singleLineOption}, multiLine=${multiLineOption}") { + dynamicTest("singleLine=$singleLineOption, multiLine=$multiLineOption") { try { // Note: if you jumped here from a failed test's stack trace, // select the "Async stack trace of TestFactory" cause's @@ -319,5 +1467,18 @@ class BracesOnIfStatementsSpec { } } } + + operator fun String.invoke(ordinal: Int): (String) -> Pair = + { code -> + fun String.next(string: String, start: Int): Int? = indexOf(string, start).takeIf { it != -1 } + + val indices = generateSequence(code.next(this, 0)) { startIndex -> + code.next(this, startIndex + 1) + } + val index = requireNotNull(indices.elementAtOrNull(ordinal - 1)) { + "There's no $ordinal. occurrence of '$this' in '$code'" + } + index to index + this.length + } } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt index 5b325bb2f17..8b47f72a371 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt @@ -7,7 +7,6 @@ import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test class MandatoryBracesIfStatementsSpec { - @Suppress("DEPRECATION") val subject = MandatoryBracesIfStatements(Config.empty) @Nested diff --git a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt index cd6d8aeb4f6..cd69600c8be 100644 --- a/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt +++ b/detekt-test/src/main/kotlin/io/gitlab/arturbosch/detekt/test/FindingsAssertions.kt @@ -71,12 +71,9 @@ class FindingsAssert(actual: List) : hasEndSourceLocations(SourceLocation(line, column)) } - fun hasTextLocations(vararg expected: Pair) = hasOffsetTextLocations(0, *expected) - - fun hasOffsetTextLocations(offset: Int, vararg expected: Pair) = apply { + fun hasTextLocations(vararg expected: Pair) = apply { val actualSources = actual.asSequence() .map { it.location.text } - .map { (start, end) -> TextLocation(start - offset, end - offset) } .sortedWith(compareBy({ it.start }, { it.end })) val expectedSources = expected.asSequence() From 6559504b5a7c8e44d1efd2736c7816e338ce4d14 Mon Sep 17 00:00:00 2001 From: "Vitaly V. Pinchuk" Date: Tue, 31 Jan 2023 19:38:27 -0400 Subject: [PATCH 15/33] Deprecate MandatoryBracesIfStatements rule --- detekt-core/src/main/resources/deprecation.properties | 1 + .../arturbosch/detekt/generator/printer/DeprecatedPrinter.kt | 1 + .../coroutines/SuspendFunWithCoroutineScopeReceiverSpec.kt | 2 ++ 3 files changed, 4 insertions(+) diff --git a/detekt-core/src/main/resources/deprecation.properties b/detekt-core/src/main/resources/deprecation.properties index 5175f2ca810..318c23293ec 100644 --- a/detekt-core/src/main/resources/deprecation.properties +++ b/detekt-core/src/main/resources/deprecation.properties @@ -17,4 +17,5 @@ formatting>TrailingComma=Rule is split between `TrailingCommaOnCallSite` and `Tr style>ForbiddenPublicDataClass=Rule migrated to `libraries` ruleset plugin style>LibraryCodeMustSpecifyReturnType=Rule migrated to `libraries` ruleset plugin style>LibraryEntitiesShouldNotBePublic=Rule migrated to `libraries` ruleset plugin +style>MandatoryBracesIfStatements=Use `BracesOnIfStatements` with `always` configuration instead complexity>ComplexMethod=Rule is renamed to `CyclomaticComplexMethod` to distinguish between Cyclomatic Complexity and Cognitive Complexity diff --git a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/DeprecatedPrinter.kt b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/DeprecatedPrinter.kt index d42c0ce5bea..e51ac0695de 100644 --- a/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/DeprecatedPrinter.kt +++ b/detekt-generator/src/main/kotlin/io/gitlab/arturbosch/detekt/generator/printer/DeprecatedPrinter.kt @@ -41,6 +41,7 @@ internal fun writeMigratedRules(): String { style>ForbiddenPublicDataClass=Rule migrated to `libraries` ruleset plugin style>LibraryCodeMustSpecifyReturnType=Rule migrated to `libraries` ruleset plugin style>LibraryEntitiesShouldNotBePublic=Rule migrated to `libraries` ruleset plugin + style>MandatoryBracesIfStatements=Use `BracesOnIfStatements` with `always` configuration instead complexity>ComplexMethod=Rule is renamed to `CyclomaticComplexMethod` to distinguish between Cyclomatic Complexity and Cognitive Complexity """.trimIndent() } diff --git a/detekt-rules-coroutines/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/coroutines/SuspendFunWithCoroutineScopeReceiverSpec.kt b/detekt-rules-coroutines/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/coroutines/SuspendFunWithCoroutineScopeReceiverSpec.kt index f8e1d1529b3..79852ae4068 100644 --- a/detekt-rules-coroutines/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/coroutines/SuspendFunWithCoroutineScopeReceiverSpec.kt +++ b/detekt-rules-coroutines/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/coroutines/SuspendFunWithCoroutineScopeReceiverSpec.kt @@ -100,6 +100,7 @@ class SuspendFunWithCoroutineScopeReceiverSpec(val env: KotlinCoreEnvironment) { val code = """ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay + import kotlinx.coroutines.launch import kotlin.time.Duration.Companion.seconds suspend fun foo() = coroutineScope { @@ -117,6 +118,7 @@ class SuspendFunWithCoroutineScopeReceiverSpec(val env: KotlinCoreEnvironment) { import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.CoroutineScope + import kotlinx.coroutines.coroutineScope suspend fun Long.foo() = coroutineScope { launch { From 788e36757c643e15908e579072840dd0c65fa0b8 Mon Sep 17 00:00:00 2001 From: "Vitaly V. Pinchuk" Date: Wed, 1 Feb 2023 20:54:58 -0400 Subject: [PATCH 16/33] Deprecate MandatoryBracesIfStatements rule --- .../main/resources/default-detekt-config.yml | 2 - .../detekt/rules/style/StyleGuideProvider.kt | 2 - .../optional/MandatoryBracesIfStatements.kt | 66 ------ .../MandatoryBracesIfStatementsSpec.kt | 217 ------------------ 4 files changed, 287 deletions(-) delete mode 100644 detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt delete mode 100644 detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt diff --git a/detekt-core/src/main/resources/default-detekt-config.yml b/detekt-core/src/main/resources/default-detekt-config.yml index edd6a69fade..559029aa147 100644 --- a/detekt-core/src/main/resources/default-detekt-config.yml +++ b/detekt-core/src/main/resources/default-detekt-config.yml @@ -602,8 +602,6 @@ style: ignoreEnums: false ignoreRanges: false ignoreExtensionFunctions: true - MandatoryBracesIfStatements: - active: false MandatoryBracesLoops: active: false MaxChainedCallsOnSameLine: diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt index 18aaf120aa1..6f10aea530d 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt @@ -4,7 +4,6 @@ import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.RuleSet import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.DefaultRuleSetProvider -import io.gitlab.arturbosch.detekt.rules.style.optional.MandatoryBracesIfStatements import io.gitlab.arturbosch.detekt.rules.style.optional.MandatoryBracesLoops import io.gitlab.arturbosch.detekt.rules.style.optional.OptionalUnit import io.gitlab.arturbosch.detekt.rules.style.optional.PreferToOverPairSyntax @@ -76,7 +75,6 @@ class StyleGuideProvider : DefaultRuleSetProvider { UnnecessaryLet(config), MayBeConst(config), PreferToOverPairSyntax(config), - MandatoryBracesIfStatements(config), BracesOnIfStatements(config), MandatoryBracesLoops(config), NullableBooleanCheck(config), diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt deleted file mode 100644 index 4ed326f97cc..00000000000 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt +++ /dev/null @@ -1,66 +0,0 @@ -package io.gitlab.arturbosch.detekt.rules.style.optional - -import io.gitlab.arturbosch.detekt.api.CodeSmell -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Debt -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 org.jetbrains.kotlin.com.intellij.psi.PsiElement -import org.jetbrains.kotlin.psi.KtBlockExpression -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtIfExpression -import org.jetbrains.kotlin.psi.KtWhenExpression -import org.jetbrains.kotlin.psi.psiUtil.siblings - -/** - * This rule detects multi-line `if` statements which do not have braces. - * Adding braces would improve readability and avoid possible errors. - * - * - * val i = 1 - * if (i > 0) - * println(i) - * - * - * - * val x = if (condition) 5 else 4 - * - */ -// @Deprecated("Use `BracesOnIfStatements` with `always` configuration instead") -class MandatoryBracesIfStatements(config: Config = Config.empty) : Rule(config) { - - override val issue = Issue( - "MandatoryBracesIfStatements", - Severity.Style, - "Multi-line if statement was found that does not have braces. " + - "These braces should be added to improve readability.", - Debt.FIVE_MINS - ) - - override fun visitIfExpression(expression: KtIfExpression) { - super.visitIfExpression(expression) - - val thenExpression = expression.then ?: return - if (thenExpression !is KtBlockExpression && hasNewLineAfter(expression.rightParenthesis)) { - report(CodeSmell(issue, Entity.from(expression.ifKeyword), issue.description)) - } - - val elseExpression = expression.`else` ?: return - if (mustBeOnSameLine(elseExpression) && hasNewLineAfter(expression.elseKeyword)) { - report(CodeSmell(issue, Entity.from(expression.elseKeyword ?: elseExpression), issue.description)) - } - } - - private fun hasNewLineAfter(element: PsiElement?): Boolean { - if (element == null) return false - return element - .siblings(forward = true, withItself = false) - .takeWhile { it.text != "else" } - .any { it.textContains('\n') } - } - - private fun mustBeOnSameLine(expression: KtExpression): Boolean = - expression !is KtIfExpression && expression !is KtBlockExpression && expression !is KtWhenExpression -} diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt deleted file mode 100644 index 8b47f72a371..00000000000 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt +++ /dev/null @@ -1,217 +0,0 @@ -package io.gitlab.arturbosch.detekt.rules.style.optional - -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.compileAndLint -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test - -class MandatoryBracesIfStatementsSpec { - val subject = MandatoryBracesIfStatements(Config.empty) - - @Nested - inner class `if statements which should have braces` { - - @Test - fun `reports a simple if`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - assertThat(findings).hasTextLocations(14 to 16) - } - - @Test - fun `reports a simple if with a single statement in multiple lines`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) 50 - .toString() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - } - - @Test - fun `reports if-else with a single statement in multiple lines`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) 50 - .toString() else 50 - .toString() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(11 to 13, 44 to 48) - } - - @Test - fun `reports if-else`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) - println() - else - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(14 to 16, 46 to 50) - } - - @Test - fun `reports if-else with else-if`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) - println() - else if (false) - println() - else - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(3) - assertThat(findings).hasTextLocations(14 to 16, 51 to 53, 84 to 88) - } - - @Test - fun `reports if with braces but else without`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) { - println() - } else - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - assertThat(findings).hasTextLocations(50 to 54) - } - - @Test - fun `reports else with braces but if without`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) - println() - else { - println() - } - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - assertThat(findings).hasTextLocations(14 to 16) - } - - @Test - fun `reports else in new line`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) println() - else println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - assertThat(findings).hasTextLocations(14 to 16) - } - - @Test - fun `reports only else body on new line`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) println() else - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - assertThat(findings).hasTextLocations(34 to 38) - } - } - - @Nested - inner class `if statements with braces` { - - @Test - fun `does not report if statements with braces`() { - val code = """ - fun f() { - if (true) { - println() - } - if (true) - { - println() - } - if (true) { println() } - } - """.trimIndent() - assertThat(subject.compileAndLint(code)).isEmpty() - } - } - - @Nested - inner class `single-line if statements which don't need braces` { - - @Test - fun `does not report single-line if statements`() { - val code = """ - fun f() { - if (true) println() - if (true) println() else println() - if (true) println() else if (false) println() else println() - } - """.trimIndent() - assertThat(subject.compileAndLint(code)).isEmpty() - } - } - - @Nested - inner class `multi-line when following an else statement without requiring braces` { - - @Test - fun `does not report multi-line when`() { - val code = """ - fun f(i: Int) { - if (true) { - println() - } else when(i) { - 1 -> println(1) - else -> println() - } - } - """.trimIndent() - assertThat(subject.compileAndLint(code)).isEmpty() - } - } -} From 305edfb5415b1219ee38ff9dad5660a25f247406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Sat, 28 Jan 2023 14:01:05 +0000 Subject: [PATCH 17/33] Detekt codebase uses AssertJ, let's use those methods. --- .../rules/style/BracesOnIfStatementsSpec.kt | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 7f0b85f583d..c5df07fa37e 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -1,5 +1,3 @@ -@file:Suppress("ForbiddenImport") - package io.gitlab.arturbosch.detekt.rules.style import io.gitlab.arturbosch.detekt.api.TextLocation @@ -9,7 +7,8 @@ import io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.Companio import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint -import org.junit.jupiter.api.Assertions +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatCode import org.junit.jupiter.api.DynamicContainer.dynamicContainer import org.junit.jupiter.api.DynamicNode import org.junit.jupiter.api.DynamicTest.dynamicTest @@ -33,13 +32,13 @@ class BracesOnIfStatementsSpec { @Test fun `validate behavior of occurrence function`() { val code = "fun f() { if (true) else if (true) if (true) true }" - Assertions.assertEquals(10 to 12, "if"(1)(code)) - Assertions.assertEquals(25 to 27, "if"(2)(code)) - Assertions.assertEquals(35 to 37, "if"(3)(code)) - Assertions.assertEquals(20 to 24, "else"(1)(code)) - Assertions.assertThrows(IllegalArgumentException::class.java) { "if"(4)(code) } - Assertions.assertThrows(IllegalArgumentException::class.java) { "if"(0)(code) } - Assertions.assertThrows(IllegalArgumentException::class.java) { "else"(2)(code) } + assertThat("if"(1)(code)).isEqualTo(10 to 12) + assertThat("if"(2)(code)).isEqualTo(25 to 27) + assertThat("if"(3)(code)).isEqualTo(35 to 37) + assertThat("else"(1)(code)).isEqualTo(20 to 24) + assertThatCode { "if"(4)(code) }.isInstanceOf(IllegalArgumentException::class.java) + assertThatCode { "if"(0)(code) }.isInstanceOf(IllegalArgumentException::class.java) + assertThatCode { "else"(2)(code) }.isInstanceOf(IllegalArgumentException::class.java) } @Nested From 1613f160e14b7890cee50dc4c7dafa5b5d203baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Sat, 18 Feb 2023 14:52:48 +0000 Subject: [PATCH 18/33] Speed up compile-test-snippets=true executions by compiling each code block only once. 1:40 -> 15 seconds --- .../detekt/rules/style/BracesOnIfStatementsSpec.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index c5df07fa37e..e712d3632e5 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -7,6 +7,7 @@ import io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.Companio import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint +import io.gitlab.arturbosch.detekt.test.lint import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatCode import org.junit.jupiter.api.DynamicContainer.dynamicContainer @@ -1411,7 +1412,11 @@ class BracesOnIfStatementsSpec { code: String, vararg locations: Pair ): DynamicNode { - val tests = createBraceTests(singleLine, multiLine) { rule -> + // Separately compile the code because otherwise all the combinations would compile them again and again. + val compileTest = dynamicTest("Compiles: $code") { + BracesOnIfStatements().compileAndLint(code) + } + val validationTests = createBraceTests(singleLine, multiLine) { rule -> rule.test(code, *locations) } val locationString = if (NOTHING.contentEquals(locations)) { @@ -1419,12 +1424,13 @@ class BracesOnIfStatementsSpec { } else { locations.map { TextLocation(it.first, it.second) }.toString() } - return dynamicContainer("flags $locationString in `$code`", tests) + return dynamicContainer("flags $locationString in `$code`", validationTests + compileTest) } private fun BracesOnIfStatements.test(code: String, vararg locations: Pair) { // This creates a 10 character prefix (signature/9, space/1) for every code example. - val findings = compileAndLint("fun f() { $code }") + // Note: not compileAndLint for performance reasons, compilation is in a separate test. + val findings = lint("fun f() { $code }") // Offset text locations by the above prefix, it results in 0-indexed locations. val offset = 10 assertThat(findings) From bd1298e9f42765f24a7ca6ea8b741bb80888fc57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Sat, 18 Feb 2023 13:27:28 +0000 Subject: [PATCH 19/33] Format --- .../rules/style/BracesOnIfStatementsSpec.kt | 98 +++++++++---------- 1 file changed, 46 insertions(+), 52 deletions(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index e712d3632e5..ceed04297fa 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -92,10 +92,7 @@ class BracesOnIfStatementsSpec { flag("if (true) { println() } else { println() }", *NOTHING), flag("if (true) { println() } else if (true) { println() }", *NOTHING), flag("if (true) { println() } else if (true) { println() } else { println() }", *NOTHING), - flag( - "if (true) { println() } else if (true) { println() } else if (true) { println() }", - *NOTHING - ), + flag("if (true) { println() } else if (true) { println() } else if (true) { println() }", *NOTHING), flag( "if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }", *NOTHING @@ -128,12 +125,13 @@ class BracesOnIfStatementsSpec { @Nested inner class `=never` { - private fun flag(code: String, vararg locations: (String) -> Pair) = testCombinations( - BracePolicy.Never.config, - NOT_RELEVANT, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + private fun flag(code: String, vararg locations: (String) -> Pair) = + testCombinations( + BracePolicy.Never.config, + NOT_RELEVANT, + code, + *(locations.map { it(code) }.toTypedArray()) + ) @TestFactory fun `no braces are accepted`() = listOf( flag("if (true) println()", *NOTHING), @@ -141,10 +139,7 @@ class BracesOnIfStatementsSpec { flag("if (true) println() else if (true) println()", *NOTHING), flag("if (true) println() else if (true) println() else println()", *NOTHING), flag("if (true) println() else if (true) println() else if (true) println()", *NOTHING), - flag( - "if (true) println() else if (true) println() else if (true) println() else println()", - *NOTHING - ), + flag("if (true) println() else if (true) println() else if (true) println() else println()", *NOTHING), ) @TestFactory @@ -246,11 +241,7 @@ class BracesOnIfStatementsSpec { fun `existing braces are flagged`() = listOf( flag("if (true) { println() }", "if"(1)), flag("if (true) { println() } else { println() }", "if"(1), "else"(1)), - flag( - "if (true) { println() } else if (true) { println() }", - "if"(1), - "if"(2) - ), + flag("if (true) { println() } else if (true) { println() }", "if"(1), "if"(2)), flag( "if (true) { println() } else if (true) { println() } else { println() }", "if"(1), @@ -419,7 +410,8 @@ class BracesOnIfStatementsSpec { println() else if (true) println() - else println() + else + println() """.trimIndent(), "if"(1), "if"(2), @@ -562,12 +554,14 @@ class BracesOnIfStatementsSpec { @Nested inner class `=never` { - private fun flag(code: String, vararg locations: (String) -> Pair) = testCombinations( - NOT_RELEVANT, - BracePolicy.Never.config, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + + private fun flag(code: String, vararg locations: (String) -> Pair) = + testCombinations( + NOT_RELEVANT, + BracePolicy.Never.config, + code, + *(locations.map { it(code) }.toTypedArray()) + ) @TestFactory fun `no braces are accepted`() = listOf( flag( @@ -777,58 +771,58 @@ class BracesOnIfStatementsSpec { // More cases, not just first branch flag( """ - if (true) - println() - else { - println() + if (true) + println() + else { + println() } """.trimIndent(), "else"(1) ), flag( """ - if (true) - println() + if (true) + println() else if (true) { - println() + println() } """.trimIndent(), "if"(2) ), flag( """ - if (true) - println() - else if (true) - println() - else { - println() + if (true) + println() + else if (true) + println() + else { + println() } """.trimIndent(), "else"(2) ), flag( """ - if (true) - println() - else if (true) - println() + if (true) + println() + else if (true) + println() else if (true) { - println() + println() } """.trimIndent(), "if"(3) ), flag( """ - if (true) - println() - else if (true) - println() - else if (true) - println() - else { - println() + if (true) + println() + else if (true) + println() + else if (true) + println() + else { + println() } """.trimIndent(), "else"(3) From 8efea302431a533884f5284c73117106fc0a0c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Sat, 18 Feb 2023 15:29:19 +0000 Subject: [PATCH 20/33] Remove duplicate code, and let the testCombinations method be used in other contexts too. --- .../rules/style/BracesOnIfStatementsSpec.kt | 73 +++++-------------- 1 file changed, 17 insertions(+), 56 deletions(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index ceed04297fa..8b771cf97dc 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -49,12 +49,7 @@ class BracesOnIfStatementsSpec { inner class `=always` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - BracePolicy.Always.config, - NOT_RELEVANT, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(BracePolicy.Always.config, NOT_RELEVANT, code, *locations) @TestFactory fun `missing braces are flagged`() = listOf( @@ -126,12 +121,7 @@ class BracesOnIfStatementsSpec { inner class `=never` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - BracePolicy.Never.config, - NOT_RELEVANT, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(BracePolicy.Never.config, NOT_RELEVANT, code, *locations) @TestFactory fun `no braces are accepted`() = listOf( flag("if (true) println()", *NOTHING), @@ -220,12 +210,7 @@ class BracesOnIfStatementsSpec { inner class `=necessary` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - BracePolicy.Necessary.config, - NOT_RELEVANT, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(BracePolicy.Necessary.config, NOT_RELEVANT, code, *locations) @TestFactory fun `no braces are accepted`() = listOf( @@ -282,12 +267,7 @@ class BracesOnIfStatementsSpec { inner class `=consistent` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - BracePolicy.Consistent.config, - NOT_RELEVANT, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(BracePolicy.Consistent.config, NOT_RELEVANT, code, *locations) @TestFactory fun `no braces are accepted`() = listOf( @@ -340,12 +320,7 @@ class BracesOnIfStatementsSpec { inner class `=always` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - NOT_RELEVANT, - BracePolicy.Always.config, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(NOT_RELEVANT, BracePolicy.Always.config, code, *locations) @TestFactory fun `missing braces are flagged`() = listOf( @@ -556,12 +531,7 @@ class BracesOnIfStatementsSpec { inner class `=never` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - NOT_RELEVANT, - BracePolicy.Never.config, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(NOT_RELEVANT, BracePolicy.Never.config, code, *locations) @TestFactory fun `no braces are accepted`() = listOf( flag( @@ -912,12 +882,7 @@ class BracesOnIfStatementsSpec { inner class `=necessary` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - NOT_RELEVANT, - BracePolicy.Necessary.config, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(NOT_RELEVANT, BracePolicy.Necessary.config, code, *locations) @TestFactory fun `no braces are accepted`() = listOf( @@ -1133,12 +1098,7 @@ class BracesOnIfStatementsSpec { inner class `=consistent` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - NOT_RELEVANT, - BracePolicy.Consistent.config, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(NOT_RELEVANT, BracePolicy.Consistent.config, code, *locations) @TestFactory fun `no braces are accepted`() = listOf( @@ -1358,15 +1318,15 @@ class BracesOnIfStatementsSpec { @TestFactory fun `whens are not flagged`() = testCombinations( - NOT_RELEVANT, - NOT_RELEVANT, - """ + singleLine = NOT_RELEVANT, + multiLine = NOT_RELEVANT, + code = """ when (true) { true -> println() else -> println() } """.trimIndent(), - *(NOTHING.map { it("") }.toTypedArray()) + locations = NOTHING ) companion object { @@ -1404,19 +1364,20 @@ class BracesOnIfStatementsSpec { singleLine: String, multiLine: String, code: String, - vararg locations: Pair + vararg locations: (String) -> Pair ): DynamicNode { + val codeLocation = locations.map { it(code) }.toTypedArray() // Separately compile the code because otherwise all the combinations would compile them again and again. val compileTest = dynamicTest("Compiles: $code") { BracesOnIfStatements().compileAndLint(code) } val validationTests = createBraceTests(singleLine, multiLine) { rule -> - rule.test(code, *locations) + rule.test(code, *codeLocation) } - val locationString = if (NOTHING.contentEquals(locations)) { + val locationString = if (NOTHING.contentEquals(codeLocation)) { "nothing" } else { - locations.map { TextLocation(it.first, it.second) }.toString() + codeLocation.map { TextLocation(it.first, it.second) }.toString() } return dynamicContainer("flags $locationString in `$code`", validationTests + compileTest) } From f9960f736542e7552e833ba21f46e73cc71a55a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Sat, 18 Feb 2023 14:39:41 +0000 Subject: [PATCH 21/33] Add some more test cases (singleLine) --- .../rules/style/BracesOnIfStatementsSpec.kt | 115 ++++++++++++++++-- 1 file changed, 104 insertions(+), 11 deletions(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 8b771cf97dc..3192f9bdd7a 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -95,7 +95,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partially missing braces are flagged`() = listOf( + fun `partially missing braces are flagged (first branch)`() = listOf( flag("if (true) { println() } else println()", "else"(1)), flag("if (true) { println() } else if (true) println()", "if"(2)), flag( @@ -115,6 +115,47 @@ class BracesOnIfStatementsSpec { "else"(3) ), ) + + @TestFactory + fun `partially missing braces are flagged (last branch)`() = listOf( + flag("if (true) println() else { println() }", "if"(1)), + flag("if (true) println() else if (true) { println() }", "if"(1)), + flag( + "if (true) println() else if (true) println() else { println() }", + "if"(1), + "if"(2) + ), + flag( + "if (true) println() else if (true) println() else if (true) { println() }", + "if"(1), + "if"(2) + ), + flag( + "if (true) println() else if (true) println() else if (true) println() else { println() }", + "if"(1), + "if"(2), + "if"(3) + ), + ) + + @TestFactory + fun `partially missing braces are flagged (middle branches)`() = listOf( + flag( + "if (true) println() else if (true) { println() } else println()", + "if"(1), + "else"(2) + ), + flag( + "if (true) println() else if (true) { println() } else if (true) println()", + "if"(1), + "if"(3) + ), + flag( + "if (true) println() else if (true) { println() } else if (true) { println() } else println()", + "if"(1), + "else"(3) + ), + ) } @Nested @@ -159,7 +200,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partially extra braces are flagged`() = listOf( + fun `partially extra braces are flagged (first branch)`() = listOf( flag("if (true) { println() }", "if"(1)), flag("if (true) { println() } else println()", "if"(1)), flag("if (true) { println() } else if (true) println()", "if"(1)), @@ -169,7 +210,10 @@ class BracesOnIfStatementsSpec { "if (true) { println() } else if (true) println() else if (true) println() else println()", "if"(1) ), - // More cases, not just first branch + ) + + @TestFactory + fun `partially extra braces are flagged (last branch)`() = listOf( flag("if (true) println() else { println() }", "else"(1)), flag("if (true) println() else if (true) { println() }", "if"(2)), flag("if (true) println() else if (true) println() else { println() }", "else"(2)), @@ -180,6 +224,17 @@ class BracesOnIfStatementsSpec { ), ) + @TestFactory + fun `partially extra braces are flagged (middle branches)`() = listOf( + flag("if (true) println() else if (true) { println() } else println()", "if"(2)), + flag("if (true) println() else if (true) { println() } else if (true) println()", "if"(2)), + flag( + "if (true) println() else if (true) { println() } else if (true) { println() } else println()", + "if"(2), + "if"(3) + ), + ) + @TestFactory fun `fully extra braces are flagged`() = listOf( flag("if (true) { println() }", "if"(1)), flag("if (true) { println() } else { println() }", "if"(1), "else"(1)), @@ -249,7 +304,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partially extra braces are flagged`() = listOf( + fun `partially extra braces are flagged (first branch)`() = listOf( flag("if (true) { println() }", "if"(1)), flag("if (true) { println() } else println()", "if"(1)), flag("if (true) { println() } else if (true) println()", "if"(1)), @@ -259,7 +314,29 @@ class BracesOnIfStatementsSpec { "if (true) { println() } else if (true) println() else if (true) println() else println()", "if"(1) ), - // TODO add more cases, not just first branch + ) + + @TestFactory + fun `partially extra braces are flagged (last branch)`() = listOf( + flag("if (true) println() else { println() }", "else"(1)), + flag("if (true) println() else if (true) { println() }", "if"(2)), + flag("if (true) println() else if (true) println() else { println() }", "else"(2)), + flag("if (true) println() else if (true) println() else if (true) { println() }", "if"(3)), + flag( + "if (true) println() else if (true) println() else if (true) println() else { println() }", + "else"(3) + ), + ) + + @TestFactory + fun `partially extra braces are flagged (middle branches)`() = listOf( + flag("if (true) println() else if (true) { println() } else println()", "if"(2)), + flag("if (true) println() else if (true) { println() } else if (true) println()", "if"(2)), + flag( + "if (true) println() else if (true) { println() } else if (true) { println() } else println()", + "if"(2), + "if"(3), + ), ) } @@ -293,7 +370,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partial braces are flagged`() = listOf( + fun `partial braces are flagged (first branch)`() = listOf( flag("if (true) { println() }", *NOTHING), flag("if (true) { println() } else println()", "if"(1)), flag("if (true) { println() } else if (true) println()", "if"(1)), @@ -303,10 +380,26 @@ class BracesOnIfStatementsSpec { "if (true) { println() } else if (true) println() else if (true) println() else println()", "if"(1) ), - // More cases, not just first branch + ) + + @TestFactory + fun `partial braces are flagged (last branch)`() = listOf( flag("if (true) println() else { println() }", "if"(1)), + flag("if (true) println() else if (true) { println() }", "if"(1)), + flag("if (true) println() else if (true) println() else { println() }", "if"(1)), + flag("if (true) println() else if (true) println() else if (true) { println() }", "if"(1)), + flag( + "if (true) println() else if (true) println() else if (true) println() else { println() }", + "if"(1), + ), + ) + + @TestFactory + fun `partial braces are flagged (middle branches)`() = listOf( + flag("if (true) println() else if (true) { println() } else println()", "if"(1)), + flag("if (true) println() else if (true) { println() } else if (true) println()", "if"(1)), flag( - "if (true) { println() } else if (true) println() else if (true) println() else { println() }", + "if (true) println() else if (true) { println() } else if (true) { println() } else println()", "if"(1) ), ) @@ -738,7 +831,7 @@ class BracesOnIfStatementsSpec { """.trimIndent(), "if"(1) ), - // More cases, not just first branch + // More cases, not just first branch. flag( """ if (true) @@ -1090,7 +1183,7 @@ class BracesOnIfStatementsSpec { """.trimIndent(), "if"(1) ), - // TODO add more cases, not just first branch + // TODO add more cases, not just first branch. ) } @@ -1297,7 +1390,7 @@ class BracesOnIfStatementsSpec { """.trimIndent(), "if"(1) ), - // More cases, not just first branch + // More cases, not just first branch. flag( """ if (true) From a436418eecee341a33841bdf661a19591433a47d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Sat, 18 Feb 2023 16:39:36 +0000 Subject: [PATCH 22/33] Add some more test cases (multiLine) --- .../rules/style/BracesOnIfStatementsSpec.kt | 354 +++++++++++++++++- 1 file changed, 346 insertions(+), 8 deletions(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 3192f9bdd7a..851e86216f5 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -559,7 +559,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partially missing braces are flagged`() = listOf( + fun `partially missing braces are flagged (first branch)`() = listOf( flag( """ if (true) { @@ -618,6 +618,114 @@ class BracesOnIfStatementsSpec { "else"(3) ), ) + + @TestFactory + fun `partially missing braces are flagged (last branch)`() = listOf( + flag( + """ + if (true) + println() + else { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "if"(1), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(1), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "if"(1), + "if"(2), + "if"(3) + ), + ) + + @TestFactory + fun `partially missing braces are flagged (middle branches)`() = listOf( + flag( + """ + if (true) + println() + else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(1), + "else"(2) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) + println() + """.trimIndent(), + "if"(1), + "if"(3) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(1), + "else"(3) + ), + ) } @Nested @@ -769,7 +877,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partially extra braces are flagged`() = listOf( + fun `partially extra braces are flagged (first branch)`() = listOf( flag( """ if (true) { @@ -831,7 +939,10 @@ class BracesOnIfStatementsSpec { """.trimIndent(), "if"(1) ), - // More cases, not just first branch. + ) + + @TestFactory + fun `partially extra braces are flagged (last branch)`() = listOf( flag( """ if (true) @@ -892,6 +1003,45 @@ class BracesOnIfStatementsSpec { ), ) + @TestFactory + fun `partially extra braces are flagged (middle branches)`() = listOf( + flag( + """ + if (true) + println() + else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) + println() + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) { + println() + } else println() + """.trimIndent(), + "if"(2), + "if"(3) + ), + ) + @TestFactory fun `fully extra braces are flagged`() = listOf( flag( """ @@ -1121,7 +1271,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partially extra braces are flagged`() = listOf( + fun `partially extra braces are flagged (first branch)`() = listOf( flag( """ if (true) { @@ -1183,7 +1333,108 @@ class BracesOnIfStatementsSpec { """.trimIndent(), "if"(1) ), - // TODO add more cases, not just first branch. + ) + + @TestFactory + fun `partially extra braces are flagged (last branch)`() = listOf( + flag( + """ + if (true) + println() + else { + println() + } + """.trimIndent(), + "else"(1) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "else"(2) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(3) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "else"(3) + ), + ) + + @TestFactory + fun `partially extra braces are flagged (middle branches)`() = listOf( + flag( + """ + if (true) + println() + else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) + println() + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(2), + "if"(3) + ), ) } @@ -1328,7 +1579,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partial braces are flagged`() = listOf( + fun `partial braces are flagged (first branch)`() = listOf( flag( """ if (true) { @@ -1360,8 +1611,8 @@ class BracesOnIfStatementsSpec { if (true) { println() } else if (true) - println() - else + println() + else println() """.trimIndent(), "if"(1) @@ -1390,6 +1641,54 @@ class BracesOnIfStatementsSpec { """.trimIndent(), "if"(1) ), + ) + + @TestFactory + fun `partial braces are flagged (last branch)`() = listOf( + flag( + """ + if (true) + println() + else { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(1) + ), // More cases, not just first branch. flag( """ @@ -1403,6 +1702,45 @@ class BracesOnIfStatementsSpec { println() } """.trimIndent(), + "if"(1), + ), + ) + + @TestFactory + fun `partial braces are flagged (middle branches)`() = listOf( + flag( + """ + if (true) + println() + else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) { + println() + } else + println() + """.trimIndent(), "if"(1) ), ) From 1309b166e9cc49b93ea78d153fd4967fe2ac4cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Sat, 18 Feb 2023 15:36:06 +0000 Subject: [PATCH 23/33] Add some more test cases --- .../rules/style/BracesOnIfStatementsSpec.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 851e86216f5..6a4cb64f7d6 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -1760,6 +1760,48 @@ class BracesOnIfStatementsSpec { locations = NOTHING ) + @TestFactory + fun `nested ifs are flagged for consistency`() = testCombinations( + singleLine = NOT_RELEVANT, + multiLine = BracePolicy.Consistent.config, + code = """ + if (true) { + if (true) { + println() + } else println() + } else println() + """.trimIndent(), + locations = arrayOf( + "if"(1), + "if"(2), + ), + ) + + @TestFactory + fun `nested ifs are flagged for always`() = testCombinations( + singleLine = BracePolicy.Always.config, + multiLine = BracePolicy.Always.config, + code = """ + if (if (true) true else false) + if (true) + println() + else + println() + else + println(if (true) true else false) + """.trimIndent(), + locations = arrayOf( + "if"(1), + "if"(2), + "else"(1), + "if"(3), + "else"(2), + "else"(3), + "if"(4), + "else"(4), + ), + ) + companion object { /** From c0926c6fe923184366edaacf213373642e625db1 Mon Sep 17 00:00:00 2001 From: "Vitaly V. Pinchuk" Date: Wed, 1 Feb 2023 20:54:58 -0400 Subject: [PATCH 24/33] Fix after merge --- .../main/resources/default-detekt-config.yml | 2 - .../detekt/rules/style/StyleGuideProvider.kt | 2 - .../optional/MandatoryBracesIfStatements.kt | 66 -- .../rules/style/BracesOnIfStatementsSpec.kt | 685 ++++++++++++++---- .../MandatoryBracesIfStatementsSpec.kt | 217 ------ 5 files changed, 559 insertions(+), 413 deletions(-) delete mode 100644 detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt delete mode 100644 detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt diff --git a/detekt-core/src/main/resources/default-detekt-config.yml b/detekt-core/src/main/resources/default-detekt-config.yml index edd6a69fade..559029aa147 100644 --- a/detekt-core/src/main/resources/default-detekt-config.yml +++ b/detekt-core/src/main/resources/default-detekt-config.yml @@ -602,8 +602,6 @@ style: ignoreEnums: false ignoreRanges: false ignoreExtensionFunctions: true - MandatoryBracesIfStatements: - active: false MandatoryBracesLoops: active: false MaxChainedCallsOnSameLine: diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt index 18aaf120aa1..6f10aea530d 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StyleGuideProvider.kt @@ -4,7 +4,6 @@ import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.RuleSet import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.DefaultRuleSetProvider -import io.gitlab.arturbosch.detekt.rules.style.optional.MandatoryBracesIfStatements import io.gitlab.arturbosch.detekt.rules.style.optional.MandatoryBracesLoops import io.gitlab.arturbosch.detekt.rules.style.optional.OptionalUnit import io.gitlab.arturbosch.detekt.rules.style.optional.PreferToOverPairSyntax @@ -76,7 +75,6 @@ class StyleGuideProvider : DefaultRuleSetProvider { UnnecessaryLet(config), MayBeConst(config), PreferToOverPairSyntax(config), - MandatoryBracesIfStatements(config), BracesOnIfStatements(config), MandatoryBracesLoops(config), NullableBooleanCheck(config), diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt deleted file mode 100644 index 4ed326f97cc..00000000000 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatements.kt +++ /dev/null @@ -1,66 +0,0 @@ -package io.gitlab.arturbosch.detekt.rules.style.optional - -import io.gitlab.arturbosch.detekt.api.CodeSmell -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.api.Debt -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 org.jetbrains.kotlin.com.intellij.psi.PsiElement -import org.jetbrains.kotlin.psi.KtBlockExpression -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtIfExpression -import org.jetbrains.kotlin.psi.KtWhenExpression -import org.jetbrains.kotlin.psi.psiUtil.siblings - -/** - * This rule detects multi-line `if` statements which do not have braces. - * Adding braces would improve readability and avoid possible errors. - * - * - * val i = 1 - * if (i > 0) - * println(i) - * - * - * - * val x = if (condition) 5 else 4 - * - */ -// @Deprecated("Use `BracesOnIfStatements` with `always` configuration instead") -class MandatoryBracesIfStatements(config: Config = Config.empty) : Rule(config) { - - override val issue = Issue( - "MandatoryBracesIfStatements", - Severity.Style, - "Multi-line if statement was found that does not have braces. " + - "These braces should be added to improve readability.", - Debt.FIVE_MINS - ) - - override fun visitIfExpression(expression: KtIfExpression) { - super.visitIfExpression(expression) - - val thenExpression = expression.then ?: return - if (thenExpression !is KtBlockExpression && hasNewLineAfter(expression.rightParenthesis)) { - report(CodeSmell(issue, Entity.from(expression.ifKeyword), issue.description)) - } - - val elseExpression = expression.`else` ?: return - if (mustBeOnSameLine(elseExpression) && hasNewLineAfter(expression.elseKeyword)) { - report(CodeSmell(issue, Entity.from(expression.elseKeyword ?: elseExpression), issue.description)) - } - } - - private fun hasNewLineAfter(element: PsiElement?): Boolean { - if (element == null) return false - return element - .siblings(forward = true, withItself = false) - .takeWhile { it.text != "else" } - .any { it.textContains('\n') } - } - - private fun mustBeOnSameLine(expression: KtExpression): Boolean = - expression !is KtIfExpression && expression !is KtBlockExpression && expression !is KtWhenExpression -} diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 7f0b85f583d..6a4cb64f7d6 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -1,5 +1,3 @@ -@file:Suppress("ForbiddenImport") - package io.gitlab.arturbosch.detekt.rules.style import io.gitlab.arturbosch.detekt.api.TextLocation @@ -9,7 +7,9 @@ import io.gitlab.arturbosch.detekt.rules.style.BracesOnIfStatementsSpec.Companio import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat import io.gitlab.arturbosch.detekt.test.compileAndLint -import org.junit.jupiter.api.Assertions +import io.gitlab.arturbosch.detekt.test.lint +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatCode import org.junit.jupiter.api.DynamicContainer.dynamicContainer import org.junit.jupiter.api.DynamicNode import org.junit.jupiter.api.DynamicTest.dynamicTest @@ -33,13 +33,13 @@ class BracesOnIfStatementsSpec { @Test fun `validate behavior of occurrence function`() { val code = "fun f() { if (true) else if (true) if (true) true }" - Assertions.assertEquals(10 to 12, "if"(1)(code)) - Assertions.assertEquals(25 to 27, "if"(2)(code)) - Assertions.assertEquals(35 to 37, "if"(3)(code)) - Assertions.assertEquals(20 to 24, "else"(1)(code)) - Assertions.assertThrows(IllegalArgumentException::class.java) { "if"(4)(code) } - Assertions.assertThrows(IllegalArgumentException::class.java) { "if"(0)(code) } - Assertions.assertThrows(IllegalArgumentException::class.java) { "else"(2)(code) } + assertThat("if"(1)(code)).isEqualTo(10 to 12) + assertThat("if"(2)(code)).isEqualTo(25 to 27) + assertThat("if"(3)(code)).isEqualTo(35 to 37) + assertThat("else"(1)(code)).isEqualTo(20 to 24) + assertThatCode { "if"(4)(code) }.isInstanceOf(IllegalArgumentException::class.java) + assertThatCode { "if"(0)(code) }.isInstanceOf(IllegalArgumentException::class.java) + assertThatCode { "else"(2)(code) }.isInstanceOf(IllegalArgumentException::class.java) } @Nested @@ -49,12 +49,7 @@ class BracesOnIfStatementsSpec { inner class `=always` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - BracePolicy.Always.config, - NOT_RELEVANT, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(BracePolicy.Always.config, NOT_RELEVANT, code, *locations) @TestFactory fun `missing braces are flagged`() = listOf( @@ -92,10 +87,7 @@ class BracesOnIfStatementsSpec { flag("if (true) { println() } else { println() }", *NOTHING), flag("if (true) { println() } else if (true) { println() }", *NOTHING), flag("if (true) { println() } else if (true) { println() } else { println() }", *NOTHING), - flag( - "if (true) { println() } else if (true) { println() } else if (true) { println() }", - *NOTHING - ), + flag("if (true) { println() } else if (true) { println() } else if (true) { println() }", *NOTHING), flag( "if (true) { println() } else if (true) { println() } else if (true) { println() } else { println() }", *NOTHING @@ -103,7 +95,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partially missing braces are flagged`() = listOf( + fun `partially missing braces are flagged (first branch)`() = listOf( flag("if (true) { println() } else println()", "else"(1)), flag("if (true) { println() } else if (true) println()", "if"(2)), flag( @@ -123,17 +115,54 @@ class BracesOnIfStatementsSpec { "else"(3) ), ) + + @TestFactory + fun `partially missing braces are flagged (last branch)`() = listOf( + flag("if (true) println() else { println() }", "if"(1)), + flag("if (true) println() else if (true) { println() }", "if"(1)), + flag( + "if (true) println() else if (true) println() else { println() }", + "if"(1), + "if"(2) + ), + flag( + "if (true) println() else if (true) println() else if (true) { println() }", + "if"(1), + "if"(2) + ), + flag( + "if (true) println() else if (true) println() else if (true) println() else { println() }", + "if"(1), + "if"(2), + "if"(3) + ), + ) + + @TestFactory + fun `partially missing braces are flagged (middle branches)`() = listOf( + flag( + "if (true) println() else if (true) { println() } else println()", + "if"(1), + "else"(2) + ), + flag( + "if (true) println() else if (true) { println() } else if (true) println()", + "if"(1), + "if"(3) + ), + flag( + "if (true) println() else if (true) { println() } else if (true) { println() } else println()", + "if"(1), + "else"(3) + ), + ) } @Nested inner class `=never` { - private fun flag(code: String, vararg locations: (String) -> Pair) = testCombinations( - BracePolicy.Never.config, - NOT_RELEVANT, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + private fun flag(code: String, vararg locations: (String) -> Pair) = + testCombinations(BracePolicy.Never.config, NOT_RELEVANT, code, *locations) @TestFactory fun `no braces are accepted`() = listOf( flag("if (true) println()", *NOTHING), @@ -141,10 +170,7 @@ class BracesOnIfStatementsSpec { flag("if (true) println() else if (true) println()", *NOTHING), flag("if (true) println() else if (true) println() else println()", *NOTHING), flag("if (true) println() else if (true) println() else if (true) println()", *NOTHING), - flag( - "if (true) println() else if (true) println() else if (true) println() else println()", - *NOTHING - ), + flag("if (true) println() else if (true) println() else if (true) println() else println()", *NOTHING), ) @TestFactory @@ -174,7 +200,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partially extra braces are flagged`() = listOf( + fun `partially extra braces are flagged (first branch)`() = listOf( flag("if (true) { println() }", "if"(1)), flag("if (true) { println() } else println()", "if"(1)), flag("if (true) { println() } else if (true) println()", "if"(1)), @@ -184,7 +210,10 @@ class BracesOnIfStatementsSpec { "if (true) { println() } else if (true) println() else if (true) println() else println()", "if"(1) ), - // More cases, not just first branch + ) + + @TestFactory + fun `partially extra braces are flagged (last branch)`() = listOf( flag("if (true) println() else { println() }", "else"(1)), flag("if (true) println() else if (true) { println() }", "if"(2)), flag("if (true) println() else if (true) println() else { println() }", "else"(2)), @@ -195,6 +224,17 @@ class BracesOnIfStatementsSpec { ), ) + @TestFactory + fun `partially extra braces are flagged (middle branches)`() = listOf( + flag("if (true) println() else if (true) { println() } else println()", "if"(2)), + flag("if (true) println() else if (true) { println() } else if (true) println()", "if"(2)), + flag( + "if (true) println() else if (true) { println() } else if (true) { println() } else println()", + "if"(2), + "if"(3) + ), + ) + @TestFactory fun `fully extra braces are flagged`() = listOf( flag("if (true) { println() }", "if"(1)), flag("if (true) { println() } else { println() }", "if"(1), "else"(1)), @@ -225,12 +265,7 @@ class BracesOnIfStatementsSpec { inner class `=necessary` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - BracePolicy.Necessary.config, - NOT_RELEVANT, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(BracePolicy.Necessary.config, NOT_RELEVANT, code, *locations) @TestFactory fun `no braces are accepted`() = listOf( @@ -246,11 +281,7 @@ class BracesOnIfStatementsSpec { fun `existing braces are flagged`() = listOf( flag("if (true) { println() }", "if"(1)), flag("if (true) { println() } else { println() }", "if"(1), "else"(1)), - flag( - "if (true) { println() } else if (true) { println() }", - "if"(1), - "if"(2) - ), + flag("if (true) { println() } else if (true) { println() }", "if"(1), "if"(2)), flag( "if (true) { println() } else if (true) { println() } else { println() }", "if"(1), @@ -273,7 +304,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partially extra braces are flagged`() = listOf( + fun `partially extra braces are flagged (first branch)`() = listOf( flag("if (true) { println() }", "if"(1)), flag("if (true) { println() } else println()", "if"(1)), flag("if (true) { println() } else if (true) println()", "if"(1)), @@ -283,7 +314,29 @@ class BracesOnIfStatementsSpec { "if (true) { println() } else if (true) println() else if (true) println() else println()", "if"(1) ), - // TODO add more cases, not just first branch + ) + + @TestFactory + fun `partially extra braces are flagged (last branch)`() = listOf( + flag("if (true) println() else { println() }", "else"(1)), + flag("if (true) println() else if (true) { println() }", "if"(2)), + flag("if (true) println() else if (true) println() else { println() }", "else"(2)), + flag("if (true) println() else if (true) println() else if (true) { println() }", "if"(3)), + flag( + "if (true) println() else if (true) println() else if (true) println() else { println() }", + "else"(3) + ), + ) + + @TestFactory + fun `partially extra braces are flagged (middle branches)`() = listOf( + flag("if (true) println() else if (true) { println() } else println()", "if"(2)), + flag("if (true) println() else if (true) { println() } else if (true) println()", "if"(2)), + flag( + "if (true) println() else if (true) { println() } else if (true) { println() } else println()", + "if"(2), + "if"(3), + ), ) } @@ -291,12 +344,7 @@ class BracesOnIfStatementsSpec { inner class `=consistent` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - BracePolicy.Consistent.config, - NOT_RELEVANT, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(BracePolicy.Consistent.config, NOT_RELEVANT, code, *locations) @TestFactory fun `no braces are accepted`() = listOf( @@ -322,7 +370,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partial braces are flagged`() = listOf( + fun `partial braces are flagged (first branch)`() = listOf( flag("if (true) { println() }", *NOTHING), flag("if (true) { println() } else println()", "if"(1)), flag("if (true) { println() } else if (true) println()", "if"(1)), @@ -332,10 +380,26 @@ class BracesOnIfStatementsSpec { "if (true) { println() } else if (true) println() else if (true) println() else println()", "if"(1) ), - // More cases, not just first branch + ) + + @TestFactory + fun `partial braces are flagged (last branch)`() = listOf( flag("if (true) println() else { println() }", "if"(1)), + flag("if (true) println() else if (true) { println() }", "if"(1)), + flag("if (true) println() else if (true) println() else { println() }", "if"(1)), + flag("if (true) println() else if (true) println() else if (true) { println() }", "if"(1)), flag( - "if (true) { println() } else if (true) println() else if (true) println() else { println() }", + "if (true) println() else if (true) println() else if (true) println() else { println() }", + "if"(1), + ), + ) + + @TestFactory + fun `partial braces are flagged (middle branches)`() = listOf( + flag("if (true) println() else if (true) { println() } else println()", "if"(1)), + flag("if (true) println() else if (true) { println() } else if (true) println()", "if"(1)), + flag( + "if (true) println() else if (true) { println() } else if (true) { println() } else println()", "if"(1) ), ) @@ -349,12 +413,7 @@ class BracesOnIfStatementsSpec { inner class `=always` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - NOT_RELEVANT, - BracePolicy.Always.config, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(NOT_RELEVANT, BracePolicy.Always.config, code, *locations) @TestFactory fun `missing braces are flagged`() = listOf( @@ -419,7 +478,8 @@ class BracesOnIfStatementsSpec { println() else if (true) println() - else println() + else + println() """.trimIndent(), "if"(1), "if"(2), @@ -499,7 +559,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partially missing braces are flagged`() = listOf( + fun `partially missing braces are flagged (first branch)`() = listOf( flag( """ if (true) { @@ -558,16 +618,121 @@ class BracesOnIfStatementsSpec { "else"(3) ), ) + + @TestFactory + fun `partially missing braces are flagged (last branch)`() = listOf( + flag( + """ + if (true) + println() + else { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "if"(1), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(1), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "if"(1), + "if"(2), + "if"(3) + ), + ) + + @TestFactory + fun `partially missing braces are flagged (middle branches)`() = listOf( + flag( + """ + if (true) + println() + else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(1), + "else"(2) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) + println() + """.trimIndent(), + "if"(1), + "if"(3) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(1), + "else"(3) + ), + ) } @Nested inner class `=never` { - private fun flag(code: String, vararg locations: (String) -> Pair) = testCombinations( - NOT_RELEVANT, - BracePolicy.Never.config, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + + private fun flag(code: String, vararg locations: (String) -> Pair) = + testCombinations(NOT_RELEVANT, BracePolicy.Never.config, code, *locations) @TestFactory fun `no braces are accepted`() = listOf( flag( @@ -712,7 +877,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partially extra braces are flagged`() = listOf( + fun `partially extra braces are flagged (first branch)`() = listOf( flag( """ if (true) { @@ -774,67 +939,109 @@ class BracesOnIfStatementsSpec { """.trimIndent(), "if"(1) ), - // More cases, not just first branch + ) + + @TestFactory + fun `partially extra braces are flagged (last branch)`() = listOf( flag( """ - if (true) - println() - else { - println() + if (true) + println() + else { + println() } """.trimIndent(), "else"(1) ), flag( """ - if (true) - println() + if (true) + println() else if (true) { - println() + println() } """.trimIndent(), "if"(2) ), flag( """ - if (true) - println() - else if (true) - println() - else { - println() + if (true) + println() + else if (true) + println() + else { + println() } """.trimIndent(), "else"(2) ), flag( """ - if (true) - println() - else if (true) - println() + if (true) + println() + else if (true) + println() else if (true) { - println() + println() } """.trimIndent(), "if"(3) ), flag( """ - if (true) - println() - else if (true) - println() - else if (true) - println() - else { - println() + if (true) + println() + else if (true) + println() + else if (true) + println() + else { + println() } """.trimIndent(), "else"(3) ), ) + @TestFactory + fun `partially extra braces are flagged (middle branches)`() = listOf( + flag( + """ + if (true) + println() + else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) + println() + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) { + println() + } else println() + """.trimIndent(), + "if"(2), + "if"(3) + ), + ) + @TestFactory fun `fully extra braces are flagged`() = listOf( flag( """ @@ -918,12 +1125,7 @@ class BracesOnIfStatementsSpec { inner class `=necessary` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - NOT_RELEVANT, - BracePolicy.Necessary.config, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(NOT_RELEVANT, BracePolicy.Necessary.config, code, *locations) @TestFactory fun `no braces are accepted`() = listOf( @@ -1069,7 +1271,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partially extra braces are flagged`() = listOf( + fun `partially extra braces are flagged (first branch)`() = listOf( flag( """ if (true) { @@ -1131,7 +1333,108 @@ class BracesOnIfStatementsSpec { """.trimIndent(), "if"(1) ), - // TODO add more cases, not just first branch + ) + + @TestFactory + fun `partially extra braces are flagged (last branch)`() = listOf( + flag( + """ + if (true) + println() + else { + println() + } + """.trimIndent(), + "else"(1) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "else"(2) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(3) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "else"(3) + ), + ) + + @TestFactory + fun `partially extra braces are flagged (middle branches)`() = listOf( + flag( + """ + if (true) + println() + else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) + println() + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(2), + "if"(3) + ), ) } @@ -1139,12 +1442,7 @@ class BracesOnIfStatementsSpec { inner class `=consistent` { private fun flag(code: String, vararg locations: (String) -> Pair) = - testCombinations( - NOT_RELEVANT, - BracePolicy.Consistent.config, - code, - *(locations.map { it(code) }.toTypedArray()) - ) + testCombinations(NOT_RELEVANT, BracePolicy.Consistent.config, code, *locations) @TestFactory fun `no braces are accepted`() = listOf( @@ -1281,7 +1579,7 @@ class BracesOnIfStatementsSpec { ) @TestFactory - fun `partial braces are flagged`() = listOf( + fun `partial braces are flagged (first branch)`() = listOf( flag( """ if (true) { @@ -1313,8 +1611,8 @@ class BracesOnIfStatementsSpec { if (true) { println() } else if (true) - println() - else + println() + else println() """.trimIndent(), "if"(1) @@ -1343,7 +1641,55 @@ class BracesOnIfStatementsSpec { """.trimIndent(), "if"(1) ), - // More cases, not just first branch + ) + + @TestFactory + fun `partial braces are flagged (last branch)`() = listOf( + flag( + """ + if (true) + println() + else { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else { + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) + println() + else if (true) { + println() + } + """.trimIndent(), + "if"(1) + ), + // More cases, not just first branch. flag( """ if (true) @@ -1356,6 +1702,45 @@ class BracesOnIfStatementsSpec { println() } """.trimIndent(), + "if"(1), + ), + ) + + @TestFactory + fun `partial braces are flagged (middle branches)`() = listOf( + flag( + """ + if (true) + println() + else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) + println() + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else if (true) { + println() + } else + println() + """.trimIndent(), "if"(1) ), ) @@ -1364,15 +1749,57 @@ class BracesOnIfStatementsSpec { @TestFactory fun `whens are not flagged`() = testCombinations( - NOT_RELEVANT, - NOT_RELEVANT, - """ + singleLine = NOT_RELEVANT, + multiLine = NOT_RELEVANT, + code = """ when (true) { true -> println() else -> println() } """.trimIndent(), - *(NOTHING.map { it("") }.toTypedArray()) + locations = NOTHING + ) + + @TestFactory + fun `nested ifs are flagged for consistency`() = testCombinations( + singleLine = NOT_RELEVANT, + multiLine = BracePolicy.Consistent.config, + code = """ + if (true) { + if (true) { + println() + } else println() + } else println() + """.trimIndent(), + locations = arrayOf( + "if"(1), + "if"(2), + ), + ) + + @TestFactory + fun `nested ifs are flagged for always`() = testCombinations( + singleLine = BracePolicy.Always.config, + multiLine = BracePolicy.Always.config, + code = """ + if (if (true) true else false) + if (true) + println() + else + println() + else + println(if (true) true else false) + """.trimIndent(), + locations = arrayOf( + "if"(1), + "if"(2), + "else"(1), + "if"(3), + "else"(2), + "else"(3), + "if"(4), + "else"(4), + ), ) companion object { @@ -1410,22 +1837,28 @@ class BracesOnIfStatementsSpec { singleLine: String, multiLine: String, code: String, - vararg locations: Pair + vararg locations: (String) -> Pair ): DynamicNode { - val tests = createBraceTests(singleLine, multiLine) { rule -> - rule.test(code, *locations) + val codeLocation = locations.map { it(code) }.toTypedArray() + // Separately compile the code because otherwise all the combinations would compile them again and again. + val compileTest = dynamicTest("Compiles: $code") { + BracesOnIfStatements().compileAndLint(code) + } + val validationTests = createBraceTests(singleLine, multiLine) { rule -> + rule.test(code, *codeLocation) } - val locationString = if (NOTHING.contentEquals(locations)) { + val locationString = if (NOTHING.contentEquals(codeLocation)) { "nothing" } else { - locations.map { TextLocation(it.first, it.second) }.toString() + codeLocation.map { TextLocation(it.first, it.second) }.toString() } - return dynamicContainer("flags $locationString in `$code`", tests) + return dynamicContainer("flags $locationString in `$code`", validationTests + compileTest) } private fun BracesOnIfStatements.test(code: String, vararg locations: Pair) { // This creates a 10 character prefix (signature/9, space/1) for every code example. - val findings = compileAndLint("fun f() { $code }") + // Note: not compileAndLint for performance reasons, compilation is in a separate test. + val findings = lint("fun f() { $code }") // Offset text locations by the above prefix, it results in 0-indexed locations. val offset = 10 assertThat(findings) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt deleted file mode 100644 index 8b47f72a371..00000000000 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/optional/MandatoryBracesIfStatementsSpec.kt +++ /dev/null @@ -1,217 +0,0 @@ -package io.gitlab.arturbosch.detekt.rules.style.optional - -import io.gitlab.arturbosch.detekt.api.Config -import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.compileAndLint -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test - -class MandatoryBracesIfStatementsSpec { - val subject = MandatoryBracesIfStatements(Config.empty) - - @Nested - inner class `if statements which should have braces` { - - @Test - fun `reports a simple if`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - assertThat(findings).hasTextLocations(14 to 16) - } - - @Test - fun `reports a simple if with a single statement in multiple lines`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) 50 - .toString() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - } - - @Test - fun `reports if-else with a single statement in multiple lines`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) 50 - .toString() else 50 - .toString() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(11 to 13, 44 to 48) - } - - @Test - fun `reports if-else`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) - println() - else - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(2) - assertThat(findings).hasTextLocations(14 to 16, 46 to 50) - } - - @Test - fun `reports if-else with else-if`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) - println() - else if (false) - println() - else - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(3) - assertThat(findings).hasTextLocations(14 to 16, 51 to 53, 84 to 88) - } - - @Test - fun `reports if with braces but else without`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) { - println() - } else - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - assertThat(findings).hasTextLocations(50 to 54) - } - - @Test - fun `reports else with braces but if without`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) - println() - else { - println() - } - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - assertThat(findings).hasTextLocations(14 to 16) - } - - @Test - fun `reports else in new line`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) println() - else println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - assertThat(findings).hasTextLocations(14 to 16) - } - - @Test - fun `reports only else body on new line`() { - val findings = subject.compileAndLint( - """ - fun f() { - if (true) println() else - println() - } - """.trimIndent() - ) - - assertThat(findings).hasSize(1) - assertThat(findings).hasTextLocations(34 to 38) - } - } - - @Nested - inner class `if statements with braces` { - - @Test - fun `does not report if statements with braces`() { - val code = """ - fun f() { - if (true) { - println() - } - if (true) - { - println() - } - if (true) { println() } - } - """.trimIndent() - assertThat(subject.compileAndLint(code)).isEmpty() - } - } - - @Nested - inner class `single-line if statements which don't need braces` { - - @Test - fun `does not report single-line if statements`() { - val code = """ - fun f() { - if (true) println() - if (true) println() else println() - if (true) println() else if (false) println() else println() - } - """.trimIndent() - assertThat(subject.compileAndLint(code)).isEmpty() - } - } - - @Nested - inner class `multi-line when following an else statement without requiring braces` { - - @Test - fun `does not report multi-line when`() { - val code = """ - fun f(i: Int) { - if (true) { - println() - } else when(i) { - 1 -> println(1) - else -> println() - } - } - """.trimIndent() - assertThat(subject.compileAndLint(code)).isEmpty() - } - } -} From 12bd6994bb563b0bb1bcd8721c3d0363cfd33d7b Mon Sep 17 00:00:00 2001 From: "Vitaly V. Pinchuk" Date: Sun, 19 Feb 2023 09:27:25 -0400 Subject: [PATCH 25/33] Fix tests --- .../arturbosch/detekt/rules/style/BracesOnIfStatements.kt | 6 +++++- .../detekt/rules/style/BracesOnIfStatementsSpec.kt | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt index 627238a381a..abcfb8575e9 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt @@ -137,7 +137,10 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { override fun visitIfExpression(expression: KtIfExpression) { super.visitIfExpression(expression) - if (expression.parent.parent is KtIfExpression) return + + val parent = expression.parent.parent + val parentCondition = (parent as? KtIfExpression)?.condition + if ((parent is KtIfExpression) && (parentCondition !== expression)) return val branches: List = walk(expression) validate(branches, policy(expression)) @@ -148,6 +151,7 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { var current: KtExpression? = expression while (current is KtIfExpression) { current.then?.let { list.add(it) } + if (current.then is KtIfExpression) { list.addAll(walk(current.then as KtIfExpression)) } // Don't add `if` because it's an `else if` which we treat as one unit. current.`else`?.takeIf { it !is KtIfExpression }?.let { list.add(it) } current = current.`else` diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 6a4cb64f7d6..b44aa947bd3 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -1783,7 +1783,7 @@ class BracesOnIfStatementsSpec { multiLine = BracePolicy.Always.config, code = """ if (if (true) true else false) - if (true) + if (if (if (true) true else false) true else false) println() else println() @@ -1795,10 +1795,14 @@ class BracesOnIfStatementsSpec { "if"(2), "else"(1), "if"(3), + "if"(4), + "if"(5), "else"(2), "else"(3), - "if"(4), "else"(4), + "else"(5), + "if"(6), + "else"(6), ), ) From 9f6732ccbdfc872875088270fee76bac5d246772 Mon Sep 17 00:00:00 2001 From: "Vitaly V. Pinchuk" Date: Mon, 20 Feb 2023 00:45:40 +0300 Subject: [PATCH 26/33] Update detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: RĂ³bert Papp --- .../arturbosch/detekt/rules/style/BracesOnIfStatements.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt index abcfb8575e9..931b17ea8fa 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt @@ -139,8 +139,9 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { super.visitIfExpression(expression) val parent = expression.parent.parent - val parentCondition = (parent as? KtIfExpression)?.condition - if ((parent is KtIfExpression) && (parentCondition !== expression)) return + // Ignore `then` and `else` branches, they're handled by the initial `if`'s visit. + // But let us process conditions, because they might be `if` themselves. + if (parent is KtIfExpression && parent.condition !== expression) return val branches: List = walk(expression) validate(branches, policy(expression)) From 006ae3d399ca8f6a8f11eaf6cd1cc63a9b1e5c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Mon, 20 Feb 2023 15:39:27 +0000 Subject: [PATCH 27/33] Add some more test cases and fix recursion --- .../rules/style/BracesOnIfStatements.kt | 7 +- .../rules/style/BracesOnIfStatementsSpec.kt | 74 ++++++++++++++++--- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt index 931b17ea8fa..04524b4c9e0 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt @@ -139,9 +139,9 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { super.visitIfExpression(expression) val parent = expression.parent.parent - // Ignore `then` and `else` branches, they're handled by the initial `if`'s visit. - // But let us process conditions, because they might be `if` themselves. - if (parent is KtIfExpression && parent.condition !== expression) return + // Ignore `else` branches, they're handled by the initial `if`'s visit. + // But let us process `then` branches and conditions, because they might be `if` themselves. + if (parent is KtIfExpression && parent.`else` === expression) return val branches: List = walk(expression) validate(branches, policy(expression)) @@ -152,7 +152,6 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { var current: KtExpression? = expression while (current is KtIfExpression) { current.then?.let { list.add(it) } - if (current.then is KtIfExpression) { list.addAll(walk(current.then as KtIfExpression)) } // Don't add `if` because it's an `else if` which we treat as one unit. current.`else`?.takeIf { it !is KtIfExpression }?.let { list.add(it) } current = current.`else` diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index b44aa947bd3..eef70654aaa 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -1689,7 +1689,6 @@ class BracesOnIfStatementsSpec { """.trimIndent(), "if"(1) ), - // More cases, not just first branch. flag( """ if (true) @@ -1777,16 +1776,42 @@ class BracesOnIfStatementsSpec { ), ) + @TestFactory + fun `only multiline ifs are are flagged (complex)`() = testCombinations( + singleLine = BracePolicy.Never.config, + multiLine = BracePolicy.Always.config, + code = """ + if (if (true) true else false) + if (true) true else false + else + println(if (true) true else false) + """.trimIndent(), + locations = arrayOf( + "if"(1), + "else"(3), + ), + ) + + @TestFactory + fun `only multiline ifs are are flagged (simple)`() = testCombinations( + singleLine = BracePolicy.Never.config, + multiLine = BracePolicy.Always.config, + code = """ + if (true) + if (true) true else false + """.trimIndent(), + locations = arrayOf( + "if"(1), + ), + ) + @TestFactory fun `nested ifs are flagged for always`() = testCombinations( singleLine = BracePolicy.Always.config, multiLine = BracePolicy.Always.config, code = """ if (if (true) true else false) - if (if (if (true) true else false) true else false) - println() - else - println() + if (true) println() else println() else println(if (true) true else false) """.trimIndent(), @@ -1795,14 +1820,43 @@ class BracesOnIfStatementsSpec { "if"(2), "else"(1), "if"(3), - "if"(4), - "if"(5), "else"(2), "else"(3), + "if"(4), "else"(4), - "else"(5), - "if"(6), - "else"(6), + ), + ) + + @TestFactory + fun `nested ifs inside condition are flagged for always`() = testCombinations( + singleLine = BracePolicy.Always.config, + multiLine = NOT_RELEVANT, + code = """ + if (if (if (true) true else false) true else false) println() + """.trimIndent(), + locations = arrayOf( + "if"(1), + "if"(2), + "if"(3), + "else"(1), + "else"(2), + ), + ) + + @TestFactory + fun `nested ifs inside then are flagged for always`() = testCombinations( + singleLine = BracePolicy.Always.config, + multiLine = NOT_RELEVANT, + code = """ + if (true) if (true) if (true) println() else println() else println() else println() + """.trimIndent(), + locations = arrayOf( + "if"(1), + "if"(2), + "if"(3), + "else"(1), + "else"(2), + "else"(3), ), ) From 9d2b90ff5f0816e0028fabb79926e034e8d5b54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Mon, 20 Feb 2023 15:45:32 +0000 Subject: [PATCH 28/33] Group nested tests into @Nested --- .../rules/style/BracesOnIfStatementsSpec.kt | 206 +++++++++--------- 1 file changed, 105 insertions(+), 101 deletions(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index eef70654aaa..43bc4502f63 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -1746,6 +1746,111 @@ class BracesOnIfStatementsSpec { } } + @Nested + inner class nested { + + @TestFactory + fun `nested ifs are flagged for consistency`() = testCombinations( + singleLine = NOT_RELEVANT, + multiLine = BracePolicy.Consistent.config, + code = """ + if (true) { + if (true) { + println() + } else println() + } else println() + """.trimIndent(), + locations = arrayOf( + "if"(1), + "if"(2), + ), + ) + + @TestFactory + fun `only multiline ifs are are flagged (complex)`() = testCombinations( + singleLine = BracePolicy.Never.config, + multiLine = BracePolicy.Always.config, + code = """ + if (if (true) true else false) + if (true) true else false + else + println(if (true) true else false) + """.trimIndent(), + locations = arrayOf( + "if"(1), + "else"(3), + ), + ) + + @TestFactory + fun `only multiline ifs are are flagged (simple)`() = testCombinations( + singleLine = BracePolicy.Never.config, + multiLine = BracePolicy.Always.config, + code = """ + if (true) + if (true) true else false + """.trimIndent(), + locations = arrayOf( + "if"(1), + ), + ) + + @TestFactory + fun `nested ifs are flagged for always`() = testCombinations( + singleLine = BracePolicy.Always.config, + multiLine = BracePolicy.Always.config, + code = """ + if (if (true) true else false) + if (true) println() else println() + else + println(if (true) true else false) + """.trimIndent(), + locations = arrayOf( + "if"(1), + "if"(2), + "else"(1), + "if"(3), + "else"(2), + "else"(3), + "if"(4), + "else"(4), + ), + ) + + @TestFactory + fun `nested ifs inside condition are flagged for always`() = testCombinations( + singleLine = BracePolicy.Always.config, + multiLine = NOT_RELEVANT, + code = """ + if (if (if (true) true else false) true else false) println() + """.trimIndent(), + locations = arrayOf( + "if"(1), + "if"(2), + "if"(3), + "else"(1), + "else"(2), + ), + ) + + @TestFactory + fun `nested ifs inside then are flagged for always`() = testCombinations( + singleLine = BracePolicy.Always.config, + multiLine = NOT_RELEVANT, + code = """ + if (true) if (true) if (true) println() else println() else println() else println() + """.trimIndent(), + locations = arrayOf( + "if"(1), + "if"(2), + "if"(3), + "else"(1), + "else"(2), + "else"(3), + ), + ) + } + @TestFactory fun `whens are not flagged`() = testCombinations( singleLine = NOT_RELEVANT, @@ -1759,107 +1864,6 @@ class BracesOnIfStatementsSpec { locations = NOTHING ) - @TestFactory - fun `nested ifs are flagged for consistency`() = testCombinations( - singleLine = NOT_RELEVANT, - multiLine = BracePolicy.Consistent.config, - code = """ - if (true) { - if (true) { - println() - } else println() - } else println() - """.trimIndent(), - locations = arrayOf( - "if"(1), - "if"(2), - ), - ) - - @TestFactory - fun `only multiline ifs are are flagged (complex)`() = testCombinations( - singleLine = BracePolicy.Never.config, - multiLine = BracePolicy.Always.config, - code = """ - if (if (true) true else false) - if (true) true else false - else - println(if (true) true else false) - """.trimIndent(), - locations = arrayOf( - "if"(1), - "else"(3), - ), - ) - - @TestFactory - fun `only multiline ifs are are flagged (simple)`() = testCombinations( - singleLine = BracePolicy.Never.config, - multiLine = BracePolicy.Always.config, - code = """ - if (true) - if (true) true else false - """.trimIndent(), - locations = arrayOf( - "if"(1), - ), - ) - - @TestFactory - fun `nested ifs are flagged for always`() = testCombinations( - singleLine = BracePolicy.Always.config, - multiLine = BracePolicy.Always.config, - code = """ - if (if (true) true else false) - if (true) println() else println() - else - println(if (true) true else false) - """.trimIndent(), - locations = arrayOf( - "if"(1), - "if"(2), - "else"(1), - "if"(3), - "else"(2), - "else"(3), - "if"(4), - "else"(4), - ), - ) - - @TestFactory - fun `nested ifs inside condition are flagged for always`() = testCombinations( - singleLine = BracePolicy.Always.config, - multiLine = NOT_RELEVANT, - code = """ - if (if (if (true) true else false) true else false) println() - """.trimIndent(), - locations = arrayOf( - "if"(1), - "if"(2), - "if"(3), - "else"(1), - "else"(2), - ), - ) - - @TestFactory - fun `nested ifs inside then are flagged for always`() = testCombinations( - singleLine = BracePolicy.Always.config, - multiLine = NOT_RELEVANT, - code = """ - if (true) if (true) if (true) println() else println() else println() else println() - """.trimIndent(), - locations = arrayOf( - "if"(1), - "if"(2), - "if"(3), - "else"(1), - "else"(2), - "else"(3), - ), - ) - companion object { /** From aa76321ceaebd1dc72a280952fd2462a7715f575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Mon, 20 Feb 2023 15:46:00 +0000 Subject: [PATCH 29/33] Remove trailing whitespace --- .../rules/style/BracesOnIfStatementsSpec.kt | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 43bc4502f63..6f5d28ddd2c 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -426,7 +426,7 @@ class BracesOnIfStatementsSpec { ), flag( """ - if (true) + if (true) println() else println() @@ -437,8 +437,8 @@ class BracesOnIfStatementsSpec { flag( """ if (true) - println() - else if (true) + println() + else if (true) println() """.trimIndent(), "if"(1), @@ -446,10 +446,10 @@ class BracesOnIfStatementsSpec { ), flag( """ - if (true) - println() + if (true) + println() else if (true) - println() + println() else println() """.trimIndent(), @@ -459,11 +459,11 @@ class BracesOnIfStatementsSpec { ), flag( """ - if (true) + if (true) println() - else if (true) + else if (true) println() - else if (true) + else if (true) println() """.trimIndent(), "if"(1), @@ -472,8 +472,8 @@ class BracesOnIfStatementsSpec { ), flag( """ - if (true) - println() + if (true) + println() else if (true) println() else if (true) @@ -502,8 +502,8 @@ class BracesOnIfStatementsSpec { """ if (true) { println() - } else { - println() + } else { + println() } """.trimIndent(), *NOTHING @@ -513,7 +513,7 @@ class BracesOnIfStatementsSpec { if (true) { println() } else if (true) { - println() + println() } """.trimIndent(), *NOTHING @@ -521,11 +521,11 @@ class BracesOnIfStatementsSpec { flag( """ if (true) { - println() + println() } else if (true) { - println() - } else { - println() + println() + } else { + println() } """.trimIndent(), *NOTHING @@ -533,11 +533,11 @@ class BracesOnIfStatementsSpec { flag( """ if (true) { - println() + println() } else if (true) { - println() + println() } else if (true) { - println() + println() } """.trimIndent(), *NOTHING @@ -545,13 +545,13 @@ class BracesOnIfStatementsSpec { flag( """ if (true) { - println() + println() } else if (true) { println() } else if (true) { println() } else { - println() + println() } """.trimIndent(), *NOTHING @@ -744,8 +744,8 @@ class BracesOnIfStatementsSpec { ), flag( """ - if (true) - println() + if (true) + println() else println() """.trimIndent(), @@ -753,9 +753,9 @@ class BracesOnIfStatementsSpec { ), flag( """ - if (true) - println() - else if (true) + if (true) + println() + else if (true) println() """.trimIndent(), *NOTHING @@ -763,21 +763,21 @@ class BracesOnIfStatementsSpec { flag( """ if (true) - println() + println() else if (true) println() - else + else println() """.trimIndent(), *NOTHING ), flag( """ - if (true) - println() - else if (true) - println() - else if (true) + if (true) + println() + else if (true) + println() + else if (true) println() """.trimIndent(), *NOTHING @@ -848,7 +848,7 @@ class BracesOnIfStatementsSpec { if (true) { println() } else if (true) { - println() + println() } else if (true) { println() } @@ -881,7 +881,7 @@ class BracesOnIfStatementsSpec { flag( """ if (true) { - println() + println() } """.trimIndent(), "if"(1) @@ -890,14 +890,14 @@ class BracesOnIfStatementsSpec { """ if (true) { println() - } else + } else println() """.trimIndent(), "if"(1) ), flag( """ - if (true) { + if (true) { println() } else if (true) println() @@ -908,9 +908,9 @@ class BracesOnIfStatementsSpec { """ if (true) { println() - } else if (true) - println() - else + } else if (true) + println() + else println() """.trimIndent(), "if"(1) @@ -918,10 +918,10 @@ class BracesOnIfStatementsSpec { flag( """ if (true) { - println() - } else if (true) - println() - else if (true) + println() + } else if (true) + println() + else if (true) println() """.trimIndent(), "if"(1) @@ -931,10 +931,10 @@ class BracesOnIfStatementsSpec { if (true) { println() } else if (true) - println() - else if (true) - println() - else + println() + else if (true) + println() + else println() """.trimIndent(), "if"(1) @@ -1046,7 +1046,7 @@ class BracesOnIfStatementsSpec { flag( """ if (true) { - println() + println() } """.trimIndent(), "if"(1) @@ -1055,8 +1055,8 @@ class BracesOnIfStatementsSpec { """ if (true) { println() - } else { - println() + } else { + println() } """.trimIndent(), "if"(1), @@ -1076,7 +1076,7 @@ class BracesOnIfStatementsSpec { flag( """ if (true) { - println() + println() } else if (true) { println() } else { @@ -1108,7 +1108,7 @@ class BracesOnIfStatementsSpec { } else if (true) { println() } else if (true) { - println() + println() } else { println() } @@ -1131,7 +1131,7 @@ class BracesOnIfStatementsSpec { fun `no braces are accepted`() = listOf( flag( """ - if (true) + if (true) println() """.trimIndent(), *NOTHING @@ -1147,7 +1147,7 @@ class BracesOnIfStatementsSpec { ), flag( """ - if (true) + if (true) println() else if (true) println() @@ -1328,7 +1328,7 @@ class BracesOnIfStatementsSpec { println() else if (true) println() - else + else println() """.trimIndent(), "if"(1) @@ -1427,7 +1427,7 @@ class BracesOnIfStatementsSpec { println() else if (true) { println() - } else if (true) { + } else if (true) { println() } else println() @@ -1496,7 +1496,7 @@ class BracesOnIfStatementsSpec { flag( """ if (true) - println() + println() else if (true) println() else if (true) @@ -1570,7 +1570,7 @@ class BracesOnIfStatementsSpec { println() } else if (true) { println() - } else { + } else { println() } """.trimIndent(), @@ -1713,7 +1713,7 @@ class BracesOnIfStatementsSpec { println() else if (true) { println() - } else + } else println() """.trimIndent(), "if"(1) From beb86562b82da6907e17be951a93522947b71f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Mon, 20 Feb 2023 17:40:05 +0000 Subject: [PATCH 30/33] Documentation of rule and internals. --- .../rules/style/BracesOnIfStatements.kt | 69 +++++++++++++++---- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt index 04524b4c9e0..29db30990cf 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt @@ -9,13 +9,14 @@ 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 org.jetbrains.kotlin.com.intellij.psi.PsiElement import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtIfExpression /** * This rule detects `if` statements which do not comply with the specified rules. - * Keeping braces consistent would improve readability and avoid possible errors. + * Keeping braces consistent will improve readability and avoid possible errors. * * The available options are: * * `always`: forces braces on all `if` and `else` branches in the whole codebase. @@ -25,18 +26,17 @@ import org.jetbrains.kotlin.psi.KtIfExpression * except where necessary for multi-statement branches. * * `never`: forces no braces on any `if` and `else` branches in the whole codebase. * - * SingleLine if-statement has no line break (\n): - * ``` + * Single-line if-statement has no line break (\n): + * ```kotlin * if (a) b else c * ``` - * MultiLine if-statement has at least one line break (\n): - * ``` + * Multi-line if-statement has at least one line break (\n): + * ```kotlin * if (a) b * else c * ``` * * - * * // singleLine = 'never' * if (a) { b } else { c } * @@ -72,6 +72,19 @@ import org.jetbrains.kotlin.psi.KtIfExpression * else { * c * } + * + * // singleLine = 'necessary' + * if (a) { b } else { c; d } + * + * // multiLine = 'necessary' + * if (a) { + * b + * c + * } else if (d) { + * e + * } else { + * f + * } * * * @@ -103,10 +116,10 @@ import org.jetbrains.kotlin.psi.KtIfExpression * } * * // singleLine = 'consistent' - * if (a) { b } else { c } - * * if (a) b else c * + * if (a) { b } else { c } + * * if (a) { b } else { c; d } * * // multiLine = 'consistent' @@ -118,6 +131,18 @@ import org.jetbrains.kotlin.psi.KtIfExpression * * if (a) b * else c + * + * // singleLine = 'necessary' + * if (a) b else { c; d } + * + * // multiLine = 'necessary' + * if (a) { + * b + * c + * } else if (d) + * e + * else + * f * */ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { @@ -138,7 +163,7 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { override fun visitIfExpression(expression: KtIfExpression) { super.visitIfExpression(expression) - val parent = expression.parent.parent + val parent = expression.parentIfCandidate() // Ignore `else` branches, they're handled by the initial `if`'s visit. // But let us process `then` branches and conditions, because they might be `if` themselves. if (parent is KtIfExpression && parent.`else` === expression) return @@ -188,7 +213,7 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { } private fun report(violator: KtExpression, policy: BracePolicy) { - val iff = violator.parent.parent as KtIfExpression + val iff = violator.parentIfCandidate() as KtIfExpression val reported = when { iff.then === violator -> iff.ifKeyword iff.`else` === violator -> iff.elseKeyword @@ -203,13 +228,33 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { report(CodeSmell(issue, Entity.from(reported ?: violator), message)) } - private fun isMultiStatement(expression: KtExpression) = + /** + * Returns a potential parent of the expression, that could be a [KtIfExpression]. + * There's a double-indirection needed because the `then` and `else` branches + * are represented as a [org.jetbrains.kotlin.psi.KtContainerNodeForControlStructureBody]. + * Also, the condition inside the `if` is in an intermediate [org.jetbrains.kotlin.psi.KtContainerNode]. + * ``` + if (parent) + * / | \ + * cond then else (parent) + * | | | + * expr expr expr + * ``` + * @see org.jetbrains.kotlin.KtNodeTypes.CONDITION + * @see org.jetbrains.kotlin.KtNodeTypes.THEN + * @see org.jetbrains.kotlin.KtNodeTypes.ELSE + */ + private fun KtExpression.parentIfCandidate(): PsiElement? = + this.parent.parent + + private fun isMultiStatement(expression: KtExpression): Boolean = expression is KtBlockExpression && expression.statements.size > 1 private fun policy(expression: KtExpression): BracePolicy = if (expression.textContains('\n')) multiLine else singleLine - private fun hasBraces(expression: KtExpression): Boolean = expression is KtBlockExpression + private fun hasBraces(expression: KtExpression): Boolean = + expression is KtBlockExpression enum class BracePolicy(val config: String) { Always("always"), From 446a9cb710ce7333236f8abf09f6362ec486e5e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Mon, 20 Feb 2023 17:42:17 +0000 Subject: [PATCH 31/33] Simplify duplicate code. --- .../detekt/rules/style/BracesOnIfStatements.kt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt index 29db30990cf..589fc6f5358 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt @@ -185,31 +185,30 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { } private fun validate(list: List, policy: BracePolicy) { - when (policy) { + val violators = when (policy) { BracePolicy.Always -> { - list.filter { !hasBraces(it) }.report(policy) + list.filter { !hasBraces(it) } } BracePolicy.Necessary -> { - list.filter { !isMultiStatement(it) && hasBraces(it) }.report(policy) + list.filter { !isMultiStatement(it) && hasBraces(it) } } BracePolicy.Never -> { - list.filter { hasBraces(it) }.report(policy) + list.filter { hasBraces(it) } } BracePolicy.Consistent -> { val braces = list.count { hasBraces(it) } val noBraces = list.count { !hasBraces(it) } if (braces != 0 && noBraces != 0) { - list.take(1).report(policy) + list.take(1) + } else { + emptyList() } } } - } - - private fun List.report(policy: BracePolicy) { - this.forEach { report(it, policy) } + violators.forEach { report(it, policy) } } private fun report(violator: KtExpression, policy: BracePolicy) { From d172b9b333d8f6710c11a487beb70d905aa70a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Mon, 20 Feb 2023 17:54:49 +0000 Subject: [PATCH 32/33] Fix detekt --- .../arturbosch/detekt/rules/style/BracesOnIfStatements.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt index 589fc6f5358..bf8f44159e9 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatements.kt @@ -72,7 +72,7 @@ import org.jetbrains.kotlin.psi.KtIfExpression * else { * c * } - * + * * // singleLine = 'necessary' * if (a) { b } else { c; d } * @@ -231,9 +231,9 @@ class BracesOnIfStatements(config: Config = Config.empty) : Rule(config) { * Returns a potential parent of the expression, that could be a [KtIfExpression]. * There's a double-indirection needed because the `then` and `else` branches * are represented as a [org.jetbrains.kotlin.psi.KtContainerNodeForControlStructureBody]. - * Also, the condition inside the `if` is in an intermediate [org.jetbrains.kotlin.psi.KtContainerNode]. + * Also, the condition inside the `if` is in an intermediate [org.jetbrains.kotlin.psi.KtContainerNode]. * ``` - if (parent) + * if (parent) * / | \ * cond then else (parent) * | | | From 4d97f6c2bb626a6d1326a7454f00221128f65164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3bert=20Papp=20=28TWiStErRob=29?= Date: Mon, 20 Feb 2023 18:03:07 +0000 Subject: [PATCH 33/33] Add missing tests for necessary policy --- .../rules/style/BracesOnIfStatementsSpec.kt | 227 ++++++++++++++++++ 1 file changed, 227 insertions(+) diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt index 6f5d28ddd2c..f1e9d9a06c2 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/BracesOnIfStatementsSpec.kt @@ -338,6 +338,41 @@ class BracesOnIfStatementsSpec { "if"(3), ), ) + + @TestFactory + fun `existing braces are not flagged when necessary`() = listOf( + flag("if (true) { println(); println() }", *NOTHING), + flag("if (true) println() else { println(); println() }", *NOTHING), + flag("if (true) println() else if (true) { println(); println() }", *NOTHING), + flag("if (true) println() else if (true) println() else { println(); println() }", *NOTHING), + ) + + @TestFactory + fun `extra braces are flagged when not necessary (first)`() = listOf( + flag("if (true) { println(); println() } else { println() }", "else"(1)), + flag("if (true) { println(); println() } else if (true) { println() }", "if"(2)), + flag("if (true) { println(); println() } else if (true) println() else { println() }", "else"(2)), + flag("if (true) { println(); println() } else if (true) { println() } else println()", "if"(2)), + flag( + "if (true) { println(); println() } else if (true) { println() } else { println() }", + "if"(2), + "else"(2) + ), + ) + + @TestFactory + fun `extra braces are flagged when not necessary (last)`() = listOf( + flag("if (true) { println() } else { println(); println() }", "if"(1)), + flag("if (true) { println() } else if (true) { println(); println() }", "if"(1)), + flag("if (true) { println() } else if (true) println() else { println(); println() }", "if"(1)), + flag("if (true) { println() } else if (true) println() else { println(); println() }", "if"(1)), + flag("if (true) println() else if (true) { println() } else { println(); println() }", "if"(2)), + flag( + "if (true) { println() } else if (true) { println() } else { println(); println() }", + "if"(1), + "if"(2) + ), + ) } @Nested @@ -1436,6 +1471,198 @@ class BracesOnIfStatementsSpec { "if"(3) ), ) + + @TestFactory + fun `existing braces are not flagged when necessary`() = listOf( + flag( + """ + if (true) { + println() + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else { + println() + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) { + println() + println() + } + """.trimIndent(), + *NOTHING + ), + flag( + """ + if (true) + println() + else if (true) + println() + else { + println() + println() + } + """.trimIndent(), + *NOTHING + ), + ) + + @TestFactory + fun `extra braces are flagged when not necessary (first)`() = listOf( + flag( + """ + if (true) { + println() + println() + } else { + println() + } + """.trimIndent(), + "else"(1) + ), + flag( + """ + if (true) { + println() + println() + } else if (true) { + println() + } + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) { + println() + println() + } else if (true) + println() + else { + println() + } + """.trimIndent(), + "else"(2) + ), + flag( + """ + if (true) { + println() + println() + } else if (true) { + println() + } else + println() + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) { + println() + println() + } else if (true) { + println() + } else { + println() + } + """.trimIndent(), + "if"(2), + "else"(2) + ), + ) + + @TestFactory + fun `extra braces are flagged when not necessary (last)`() = listOf( + flag( + """ + if (true) { + println() + } else { + println() + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else { + println() + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) { + println() + } else if (true) + println() + else { + println() + println() + } + """.trimIndent(), + "if"(1) + ), + flag( + """ + if (true) + println() + else if (true) { + println() + } else { + println() + println() + } + """.trimIndent(), + "if"(2) + ), + flag( + """ + if (true) { + println() + } else if (true) { + println() + } else { + println() + println() + } + """.trimIndent(), + "if"(1), + "if"(2) + ), + ) } @Nested