Repository: analysis-dev/diktat (kotlin)

Issue:
`AVOID_NULL_CHECKS` conflicts with `NO_BRACES_IN_CONDITIONALS_AND_LOOPS`
## Describe the bug
```kotlin
fun foo() {
    if (a) {
        bar()
    } else if (b != null) {
        baz()
    } else {
        qux()
    }
}
```

## Observed behavior
Diktat changes `else` to `let` and then complains that there are no braces
```
...
Warn (4:7) [NO_BRACES_IN_CONDITIONALS_AND_LOOPS] in if, else, when, for, do, and while statements braces should be used. Exception: single line if statement.: ELSE
...
```
```kotlin
fun foo() {
    if (a) {
        bar()
    } else b?.let {
        baz()
    }
        ?: run {
            qux()
        }
}
```

## Environment information
* diktat version: 1.0.1

Pull request:
### What's done:
* Don't trigger on scope function, since they are already have braces

Patch:
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BracesInConditionalsAndLoopsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BracesInConditionalsAndLoopsRule.kt
index 2bdc259935..69919c8635 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BracesInConditionalsAndLoopsRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BracesInConditionalsAndLoopsRule.kt
@@ -9,12 +9,15 @@ import org.cqfn.diktat.ruleset.utils.loopType
 
 import com.pinterest.ktlint.core.ast.ElementType
 import com.pinterest.ktlint.core.ast.ElementType.BLOCK
+import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION
 import com.pinterest.ktlint.core.ast.ElementType.DO_KEYWORD
 import com.pinterest.ktlint.core.ast.ElementType.ELSE_KEYWORD
 import com.pinterest.ktlint.core.ast.ElementType.IF
 import com.pinterest.ktlint.core.ast.ElementType.IF_KEYWORD
 import com.pinterest.ktlint.core.ast.ElementType.LBRACE
 import com.pinterest.ktlint.core.ast.ElementType.RBRACE
+import com.pinterest.ktlint.core.ast.ElementType.REFERENCE_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS_EXPRESSION
 import com.pinterest.ktlint.core.ast.ElementType.WHEN
 import com.pinterest.ktlint.core.ast.ElementType.WHILE_KEYWORD
 import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
@@ -30,6 +33,7 @@ import org.jetbrains.kotlin.psi.KtIfExpression
 import org.jetbrains.kotlin.psi.KtLoopExpression
 import org.jetbrains.kotlin.psi.KtWhenExpression
 import org.jetbrains.kotlin.psi.psiUtil.astReplace
+import org.jetbrains.kotlin.psi.psiUtil.children
 
 /**
  * Rule that checks that all conditionals and loops have braces.
@@ -90,6 +94,24 @@ class BracesInConditionalsAndLoopsRule(configRules: List<RulesConfig>) : DiktatR
         }
 
         if (elseKeyword != null && elseNode?.elementType != IF && elseNode?.elementType != BLOCK) {
+            // Looking for scope functions, for which we won't trigger
+            val callAndSafeAccessExpressionChildren = elseNode?.findChildrenMatching {
+                it.elementType == CALL_EXPRESSION || it.elementType == SAFE_ACCESS_EXPRESSION
+            }
+
+            val scopeFunctionChildren = callAndSafeAccessExpressionChildren?.flatMap {
+                it.children()
+            }?.filter {
+                it.elementType == REFERENCE_EXPRESSION
+            }
+
+            val isNodeHaveScopeFunctionChildren = scopeFunctionChildren?.any {
+                it.text in scopeFunctions
+            }
+            if (isNodeHaveScopeFunctionChildren == true) {
+                return
+            }
+
             NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnAndFix(configRules, emitWarn, isFixMode, "ELSE",
                 (elseNode?.treeParent?.prevSibling { it.elementType == ELSE_KEYWORD } ?: node).startOffset, node) {
                 elseNode?.run {
@@ -171,5 +193,6 @@ class BracesInConditionalsAndLoopsRule(configRules: List<RulesConfig>) : DiktatR
     }
     companion object {
         private const val INDENT_STEP = 4
+        private val scopeFunctions = listOf("let", "run", "apply", "also")
     }
 }


Test patch:
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/BracesRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/BracesRuleWarnTest.kt
index 5c6106d105..b3be6b18a5 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/BracesRuleWarnTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/BracesRuleWarnTest.kt
@@ -94,6 +94,104 @@ class BracesRuleWarnTest : LintTestBase(::BracesInConditionalsAndLoopsRule) {
         )
     }
 
+    @Test
+    @Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
+    fun `should check braces in if statements - exception for let`() {
+        lintMethod(
+            """
+            |fun foo() {
+            |    if (a) {
+            |        bar()
+            |    } else b?.let {
+            |        baz()
+            |    }
+            |        ?: run {
+            |            qux()
+            |        }
+            |}
+            """.trimMargin()
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
+    fun `should check braces in if statements - exception for let 2`() {
+        lintMethod(
+            """
+            |fun foo() {
+            |    if (a) {
+            |        bar()
+            |    } else b?.let {
+            |        baz()
+            |    }
+            |}
+            """.trimMargin()
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
+    fun `should check braces in if statements - exception for run`() {
+        lintMethod(
+            """
+            |fun foo() {
+            |    if (a) {
+            |        bar()
+            |    } else b!!.run {
+            |        baz()
+            |    }
+            |}
+            """.trimMargin()
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
+    fun `should check braces in if statements - exception for apply`() {
+        lintMethod(
+            """
+            |fun foo() {
+            |    if (a) {
+            |        bar()
+            |    } else b.apply {
+            |        baz()
+            |    }
+            |}
+            """.trimMargin()
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
+    fun `should check braces in if statements, apply exists, but braces are needed`() {
+        lintMethod(
+            """
+            |fun foo() {
+            |    if (a) {
+            |        bar()
+            |    } else baz(b.apply { id = 5 })
+            |}
+            """.trimMargin(),
+            LintError(4, 7, ruleId, "${NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnText()} ELSE", true)
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
+    fun `should check braces in if statements, apply exists, but braces are needed 2`() {
+        lintMethod(
+            """
+            |fun foo() {
+            |    if (a) {
+            |        bar()
+            |    } else 
+            |        c.baz(b.apply {id = 5})
+            |}
+            """.trimMargin(),
+            LintError(4, 7, ruleId, "${NO_BRACES_IN_CONDITIONALS_AND_LOOPS.warnText()} ELSE", true)
+        )
+    }
+
     @Test
     @Tag(WarningNames.NO_BRACES_IN_CONDITIONALS_AND_LOOPS)
     fun `should correctly detect single line if`() {
diff --git a/diktat-rules/src/test/resources/test/paragraph3/braces/IfElseBraces1Expected.kt b/diktat-rules/src/test/resources/test/paragraph3/braces/IfElseBraces1Expected.kt
index 0214196567..847f22c8a4 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/braces/IfElseBraces1Expected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/braces/IfElseBraces1Expected.kt
@@ -28,4 +28,31 @@ fun foo4() {
     if (x > 0) {
     } else {
     } ;
-}
\ No newline at end of file
+}
+
+fun foo() {
+    if (a) {
+        bar()
+    } else b?.let {
+        baz()
+    }
+        ?: run {
+            qux()
+        }
+}
+
+fun foo() {
+    if (a) {
+        bar()
+    } else b.apply {
+        baz()
+    }
+}
+
+fun foo() {
+    if (a) {
+        bar()
+    } else {
+        baz(b.apply { id = 5 })
+    }
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph3/braces/IfElseBraces1Test.kt b/diktat-rules/src/test/resources/test/paragraph3/braces/IfElseBraces1Test.kt
index 8c516d936e..98d639318d 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/braces/IfElseBraces1Test.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/braces/IfElseBraces1Test.kt
@@ -23,4 +23,29 @@ fun foo3() {
 fun foo4() {
     if (x > 0)
     else ;
-}
\ No newline at end of file
+}
+
+fun foo() {
+    if (a) {
+        bar()
+    } else b?.let {
+        baz()
+    }
+        ?: run {
+            qux()
+        }
+}
+
+fun foo() {
+    if (a) {
+        bar()
+    } else b.apply {
+        baz()
+    }
+}
+
+fun foo() {
+    if (a) {
+        bar()
+    } else baz(b.apply { id = 5 })
+}
diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Expected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Expected.kt
index ca41e20f67..7887176225 100644
--- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Expected.kt
+++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Expected.kt
@@ -20,3 +20,14 @@ fun foo() {
             doSmth()
         }
 }
+
+fun fooo() {
+    if (a) {
+        bar()
+    } else b?.let {
+        baz()
+    }
+        ?: run {
+            qux()
+        }
+}
diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Test.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Test.kt
index cd2f5d98df..d3d37b5ae9 100644
--- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Test.kt
+++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example7Test.kt
@@ -20,3 +20,14 @@ fun foo() {
     }
 }
 
+fun fooo() {
+    if (a) {
+        bar()
+    } else b?.let {
+        baz()
+    }
+        ?: run {
+            qux()
+        }
+}
+