Repository: cqfn/diKTat (kotlin)

Issue:
`WRONG_INDENTATION`: `extendedIndentAfterOperators` ignored if lines are wrapped before a binary operator or an infix function
Consider this fragment (originally from https://github.com/saveourtool/diktat/issues/1312#issuecomment-1140119752)

```kotlin
systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE
        or View.SYSTEM_UI_FLAG_FULLSCREEN
        or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
        or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
```

Here, infix function calls are wrapped _before_ the operator (the infix function), and `extendedIndentAfterOperators` has no effect (DiKTat always expects a _single_ indent, not a _continuation_ indent).

The workaround (fir version 1.1.0) is to reformat the code as follows (https://github.com/saveourtool/diktat/issues/1312#issuecomment-1142337550):

```kotlin
systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE or
        View.SYSTEM_UI_FLAG_FULLSCREEN or
        View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
        View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
        View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
```

The expected behaviour is that `extendedIndentAfterOperators` is honored regardless of whether the code is wrapped _before_ or _after_ a binary operator.

Other examples (the code style assumes `extendedIndentAfterOperators` is **on**):

```kotlin
const val FOO = 1

const val BAR = 2

const val BAZ = 4

fun acceptInteger(arg: Int) = Unit

fun main() {
    acceptInteger(FOO or BAR or BAZ or FOO or BAR or BAZ
            or FOO or BAR or BAZ or FOO or BAR or BAZ or FOO or BAR or BAZ
            or FOO or BAR or BAZ)
}
```

```kotlin
const val TRUE = true

const val FALSE = false

fun acceptBoolean(arg: Boolean) = Unit

fun main() {
    acceptBoolean(TRUE
            || FALSE
            || TRUE)
}
```

```kotlin
val c = (3
        + 2)
```

```kotlin
infix fun Int.combineWith(that: Int) = this + that

fun main() {
    val x = (1
            combineWith 2
            combineWith 3
            combineWith 4
            combineWith 5
            combineWith 6)
}
```

# `when`- and `for`-statements

The argument of a `when`-expression:

```kotlin
when (true
        || false) {
    true -> Unit
    else -> Unit
}
```

The branch condition of a `when`-expression:

```kotlin
when ("".isEmpty()) {
    (true
            || false) -> Unit
    else -> Unit
}
```

Infix function calls in a `for`-loop:

```kotlin
for (i in 6
        downTo 0
        step 2) {
    return
}
```

Operator function calls in a `for`-loop:

```kotlin
for (i in 1
        ..42) {
    return
}
```

# More examples

```kotlin
true
        || false

val a = true
        || false
```

# Expressions wrapped in parentheses

Affected by the fix for #1409, see [this comment](https://github.com/saveourtool/diktat/issues/1340#issuecomment-1170026704).

```kotlin
(true
        || false)

val a = (true
        || false)
```

# `if`-conditions (should be controlled by a separate flag)

A slightly modified example from IDEA code style settings (`CONTINUATION_INDENT_IN_IF_CONDITIONS` is **on**):

```kotlin
fun foo1(i1: Int, i2: Int, i3: Int): Int {
    if (i2 > 0
            && i3 < 0) {
        return 2
    }
    return 0
}
```

Minimal repro:

```kotlin
if (true
        || false) {
    return
}
```

or 

```kotlin
while (true
        || false) {
    return
}
```
## Environment information
* diktat version: 1.1.0
* build tool (maven/gradle): _Maven_
* how is diktat run (CLI, plugin, etc.): `mvn diktat:check`
* kotlin version: **1.6.21**
* operating system: Windows 10

Pull request description:
### What's done:

 * Now, the indentation in binary expressions wrapped *before* a binary operator
   or an infix function is also controlled with `extendedIndentAfterOperators`.
 * The above is also true for `as` and `as?` operators.
 * The only exclusion is the Elvis operator (`?:`).
 * Fixes #1340.

Patch:
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt
index f9c1abc302..24e7dfe288 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/kdoc/KdocMethods.kt
@@ -170,7 +170,7 @@ class KdocMethods(configRules: List<RulesConfig>) : DiktatRule(
         val isReferenceExpressionWithSameName = node.findAllDescendantsWithSpecificType(REFERENCE_EXPRESSION).map { it.text }.contains((node.psi as KtFunction).name)
         val hasReturnKdoc = kdocTags != null && kdocTags.hasKnownKdocTag(KDocKnownTag.RETURN)
         return (hasExplicitNotUnitReturnType || isFunWithExpressionBody && !hasExplicitUnitReturnType && hasNotExpressionBodyTypes)
-        && !hasReturnKdoc && !isReferenceExpressionWithSameName
+                && !hasReturnKdoc && !isReferenceExpressionWithSameName
     }
 
     private fun getExplicitlyThrownExceptions(node: ASTNode): Set<String> {
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/SmartCastRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/SmartCastRule.kt
index 45bae7aa8b..943fc6678d 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/SmartCastRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter4/SmartCastRule.kt
@@ -126,7 +126,7 @@ class SmartCastRule(configRules: List<RulesConfig>) : DiktatRule(
             val list: MutableList<KtNameReferenceExpression> = mutableListOf()
             asExpr.forEach { asCall ->
                 if (asCall.node.findParentNodeWithSpecificType(IF)
-                    == it.node.findParentNodeWithSpecificType(IF)) {
+                        == it.node.findParentNodeWithSpecificType(IF)) {
                     list.add(asCall)
                 }
             }
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt
index 348da4a767..a6c6fdf8dd 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstNodeUtils.kt
@@ -930,7 +930,7 @@ private fun ASTNode.hasExplicitIt(): Boolean {
     val parameterList = findChildByType(ElementType.FUNCTION_LITERAL)
         ?.findChildByType(ElementType.VALUE_PARAMETER_LIST)
         ?.psi
-        as KtParameterList?
+            as KtParameterList?
     return parameterList?.parameters
         ?.any { it.name == "it" }
         ?: false
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/indentation/Checkers.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/indentation/Checkers.kt
index 99ebd2458d..e869c7247f 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/indentation/Checkers.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/indentation/Checkers.kt
@@ -14,6 +14,7 @@ import com.pinterest.ktlint.core.ast.ElementType.ARROW
 import com.pinterest.ktlint.core.ast.ElementType.AS_KEYWORD
 import com.pinterest.ktlint.core.ast.ElementType.AS_SAFE
 import com.pinterest.ktlint.core.ast.ElementType.BINARY_EXPRESSION
+import com.pinterest.ktlint.core.ast.ElementType.BINARY_WITH_TYPE
 import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT
 import com.pinterest.ktlint.core.ast.ElementType.BODY
 import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION
@@ -39,6 +40,7 @@ import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST
 import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER
 import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER_LIST
 import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
+import com.pinterest.ktlint.core.ast.children
 import com.pinterest.ktlint.core.ast.nextCodeSibling
 import com.pinterest.ktlint.core.ast.prevSibling
 import org.jetbrains.kotlin.com.intellij.lang.ASTNode
@@ -138,7 +140,17 @@ internal class ValueParameterListChecker(configuration: IndentationConfig) : Cus
 internal class ExpressionIndentationChecker(configuration: IndentationConfig) : CustomIndentationChecker(configuration) {
     override fun checkNode(whiteSpace: PsiWhiteSpace, indentError: IndentationError): CheckResult? =
         when {
-            whiteSpace.parent.node.elementType == BINARY_EXPRESSION && whiteSpace.prevSibling.node.elementType == OPERATION_REFERENCE -> {
+            whiteSpace.parent.node.elementType in sequenceOf(BINARY_EXPRESSION, BINARY_WITH_TYPE) &&
+                    whiteSpace.immediateSiblings().any { sibling ->
+                        /*
+                         * We're looking for an operation reference, including
+                         * `as` and `as?` (`AS_SAFE`), but excluding `?:` (`ELVIS`),
+                         * because there's a separate flag for Elvis expressions
+                         * in IDEA (`CONTINUATION_INDENT_IN_ELVIS`).
+                         */
+                        sibling.node.elementType == OPERATION_REFERENCE &&
+                                sibling.node.children().firstOrNull()?.elementType != ELVIS
+                    } -> {
                 val parentIndent = whiteSpace.parentIndent() ?: indentError.expected
                 val expectedIndent = parentIndent + IndentationAmount.valueOf(configuration.extendedIndentAfterOperators)
                 CheckResult.from(indentError.actual, expectedIndent, true)
@@ -308,3 +320,10 @@ internal fun PsiElement.parentIndent(): Int? = parentsWithSelf
     .firstOrNull()
     ?.text
     ?.lastIndent()
+
+/**
+ * @return the sequence of immediate siblings (the previous and the next one),
+ *   excluding `null`'s.
+ */
+private fun PsiElement.immediateSiblings(): Sequence<PsiElement> =
+    sequenceOf(prevSibling, nextSibling).filterNotNull()


Test patch:
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleFixTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleFixTest.kt
index 1359a90e93..81ab97c7f8 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleFixTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleFixTest.kt
@@ -11,6 +11,7 @@ import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestMixin.withCust
 import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.dotQualifiedExpressions
 import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.expressionBodyFunctions
 import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.expressionsWrappedAfterOperator
+import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.expressionsWrappedBeforeOperator
 import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.parenthesesSurroundedInfixExpressions
 import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.whitespaceInStringLiterals
 import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_INDENTATION
@@ -225,6 +226,40 @@ class IndentationRuleFixTest : FixTestBase("test/paragraph3/indentation",
         }
     }
 
+    /**
+     * See [#1340](https://github.com/saveourtool/diktat/issues/1340).
+     */
+    @Nested
+    @TestMethodOrder(DisplayName::class)
+    inner class `Expressions wrapped before operator` {
+        @ParameterizedTest(name = "$EXTENDED_INDENT_AFTER_OPERATORS = {0}")
+        @ValueSource(booleans = [false, true])
+        @Tag(WarningNames.WRONG_INDENTATION)
+        fun `should be properly indented`(extendedIndentAfterOperators: Boolean, @TempDir tempDir: Path) {
+            val defaultConfig = IndentationConfig(NEWLINE_AT_END to false)
+            val customConfig = defaultConfig.withCustomParameters(EXTENDED_INDENT_AFTER_OPERATORS to extendedIndentAfterOperators)
+
+            lintMultipleMethods(
+                expressionsWrappedBeforeOperator[extendedIndentAfterOperators].assertNotNull(),
+                tempDir = tempDir,
+                rulesConfigList = customConfig.asRulesConfigList())
+        }
+
+        @ParameterizedTest(name = "$EXTENDED_INDENT_AFTER_OPERATORS = {0}")
+        @ValueSource(booleans = [false, true])
+        @Tag(WarningNames.WRONG_INDENTATION)
+        fun `should be reformatted if mis-indented`(extendedIndentAfterOperators: Boolean, @TempDir tempDir: Path) {
+            val defaultConfig = IndentationConfig(NEWLINE_AT_END to false)
+            val customConfig = defaultConfig.withCustomParameters(EXTENDED_INDENT_AFTER_OPERATORS to extendedIndentAfterOperators)
+
+            lintMultipleMethods(
+                actualContent = expressionsWrappedBeforeOperator[!extendedIndentAfterOperators].assertNotNull(),
+                expectedContent = expressionsWrappedBeforeOperator[extendedIndentAfterOperators].assertNotNull(),
+                tempDir = tempDir,
+                rulesConfigList = customConfig.asRulesConfigList())
+        }
+    }
+
     /**
      * See [#1409](https://github.com/saveourtool/diktat/issues/1409).
      */
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleTestResources.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleTestResources.kt
index f7ddad9dcb..24f95506da 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleTestResources.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleTestResources.kt
@@ -605,6 +605,29 @@ internal object IndentationRuleTestResources {
         |            2 to
         |            3))
         """.trimMargin(),
+
+        """
+        |fun f() {
+        |    g(42 as
+        |        Integer)
+        |}
+        """.trimMargin(),
+
+        """
+        |fun f() {
+        |    g("" as?
+        |        String?)
+        |}
+        """.trimMargin(),
+
+        """
+        |fun f() {
+        |    // The dot-qualified expression is always single-indented. 
+        |    ""
+        |        .length as
+        |        Int
+        |}
+        """.trimMargin(),
     )
 
     /**
@@ -842,6 +865,29 @@ internal object IndentationRuleTestResources {
         |                2 to
         |                3))
         """.trimMargin(),
+
+        """
+        |fun f() {
+        |    g(42 as
+        |            Integer)
+        |}
+        """.trimMargin(),
+
+        """
+        |fun f() {
+        |    g("" as?
+        |            String?)
+        |}
+        """.trimMargin(),
+
+        """
+        |fun f() {
+        |    // The dot-qualified expression is always single-indented. 
+        |    ""
+        |        .length as
+        |            Int
+        |}
+        """.trimMargin(),
     )
 
     /**
@@ -853,6 +899,491 @@ internal object IndentationRuleTestResources {
         false to expressionsWrappedAfterOperatorSingleIndent,
         true to expressionsWrappedAfterOperatorContinuationIndent)
 
+    /**
+     * Expressions wrapped before an operator or an infix function, single
+     * indent (`extendedIndentAfterOperators` is **off**).
+     *
+     * When adding new code fragments to this list, be sure to also add their
+     * counterparts (preserving order) to [expressionsWrappedBeforeOperatorContinuationIndent].
+     *
+     * See [#1340](https://github.com/saveourtool/diktat/issues/1340).
+     *
+     * @see expressionsWrappedBeforeOperatorContinuationIndent
+     */
+    @Language("kotlin")
+    private val expressionsWrappedBeforeOperatorSingleIndent = arrayOf(
+        """
+        |fun f() {
+        |    systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE
+        |        or View.SYSTEM_UI_FLAG_FULLSCREEN
+        |        or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+        |        or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+        |        or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+        |        or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
+        |}
+        """.trimMargin(),
+
+        """
+        |val systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE
+        |    or View.SYSTEM_UI_FLAG_FULLSCREEN
+        |    or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+        |    or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+        |    or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+        |    or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
+        """.trimMargin(),
+
+        """
+        |const val FOO = 1
+        |
+        |const val BAR = 2
+        |
+        |const val BAZ = 4
+        |
+        |fun acceptInteger(arg: Int) = Unit
+        |
+        |fun main() {
+        |    acceptInteger(FOO or BAR or BAZ or FOO or BAR or BAZ
+        |        or FOO or BAR or BAZ or FOO or BAR or BAZ or FOO or BAR or BAZ
+        |        or FOO or BAR or BAZ)
+        |}
+        """.trimMargin(),
+
+        """
+        |const val TRUE = true
+        |
+        |const val FALSE = false
+        |
+        |fun acceptBoolean(arg: Boolean) = Unit
+        |
+        |fun f() {
+        |    acceptBoolean(TRUE
+        |        || FALSE
+        |        || TRUE)
+        |}
+        """.trimMargin(),
+
+        """
+        |val c = (3
+        |    + 2)
+        """.trimMargin(),
+
+        """
+        |infix fun Int.combineWith(that: Int) = this + that
+        |
+        |fun f() {
+        |    val x = (1
+        |        combineWith 2
+        |        combineWith 3
+        |        combineWith 4
+        |        combineWith 5
+        |        combineWith 6)
+        |}
+        """.trimMargin(),
+
+        """
+        |fun f(i1: Int, i2: Int, i3: Int): Int {
+        |    if (i2 > 0
+        |        && i3 < 0) {
+        |        return 2
+        |    }
+        |    return 0
+        |}
+        """.trimMargin(),
+
+        """
+        |val value1 = (1
+        |    to 2
+        |    to 3)
+        """.trimMargin(),
+
+        """
+        |val value2 =
+        |    (1
+        |        to 2
+        |        to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value3 = identity(1
+        |    to 2
+        |    to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value4 = identity(
+        |    1
+        |        to 2
+        |        to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value5 =
+        |    identity(1
+        |        to 2
+        |        to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |/**
+        | * Line breaks:
+        | *
+        | * 1. before the expression body (`=`),
+        | * 2. before the effective function arguments, and
+        | * 3. on each infix function call ([to]).
+        | */
+        |val value6 =
+        |    identity(
+        |        1
+        |            to 2
+        |            to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value7 = identity(identity(1
+        |    to 2
+        |    to 3))
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value8 = identity(identity(
+        |    1
+        |        to 2
+        |        to 3))
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value9 =
+        |    identity(identity(1
+        |        to 2
+        |        to 3))
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value10 =
+        |    identity(identity(
+        |        1
+        |            to 2
+        |            to 3))
+        """.trimMargin(),
+
+        """
+        |// Same as above, but using a custom getter instead of an explicit initializer.
+        |val value11
+        |    get() =
+        |        (1
+        |            to 2
+        |            to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |// Same as above, but using a custom getter instead of an explicit initializer.
+        |val value12
+        |    get() =
+        |        identity(1
+        |            to 2
+        |            to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |// Same as above, but using a custom getter instead of an explicit initializer.
+        |val value13
+        |    get() =
+        |        identity(identity(1
+        |            to 2
+        |            to 3))
+        """.trimMargin(),
+
+        """
+        |fun f() {
+        |    g(42
+        |        as Integer)
+        |}
+        """.trimMargin(),
+
+        """
+        |fun f() {
+        |    g(""
+        |        as? String?)
+        |}
+        """.trimMargin(),
+
+        """
+        |fun f() {
+        |    // The dot-qualified expression is always single-indented. 
+        |    ""
+        |        .length
+        |        as Int
+        |}
+        """.trimMargin(),
+    )
+
+    /**
+     * Expressions wrapped before an operator or an infix function, continuation
+     * indent (`extendedIndentAfterOperators` is **on**).
+     *
+     * When adding new code fragments to this list, be sure to also add their
+     * counterparts (preserving order) to [expressionsWrappedBeforeOperatorSingleIndent].
+     *
+     * See [#1340](https://github.com/saveourtool/diktat/issues/1340).
+     *
+     * @see expressionsWrappedBeforeOperatorSingleIndent
+     */
+    @Language("kotlin")
+    private val expressionsWrappedBeforeOperatorContinuationIndent = arrayOf(
+        """
+        |fun f() {
+        |    systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE
+        |            or View.SYSTEM_UI_FLAG_FULLSCREEN
+        |            or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+        |            or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+        |            or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+        |            or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
+        |}
+        """.trimMargin(),
+
+        """
+        |val systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE
+        |        or View.SYSTEM_UI_FLAG_FULLSCREEN
+        |        or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+        |        or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+        |        or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+        |        or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
+        """.trimMargin(),
+
+        """
+        |const val FOO = 1
+        |
+        |const val BAR = 2
+        |
+        |const val BAZ = 4
+        |
+        |fun acceptInteger(arg: Int) = Unit
+        |
+        |fun main() {
+        |    acceptInteger(FOO or BAR or BAZ or FOO or BAR or BAZ
+        |            or FOO or BAR or BAZ or FOO or BAR or BAZ or FOO or BAR or BAZ
+        |            or FOO or BAR or BAZ)
+        |}
+        """.trimMargin(),
+
+        """
+        |const val TRUE = true
+        |
+        |const val FALSE = false
+        |
+        |fun acceptBoolean(arg: Boolean) = Unit
+        |
+        |fun f() {
+        |    acceptBoolean(TRUE
+        |            || FALSE
+        |            || TRUE)
+        |}
+        """.trimMargin(),
+
+        """
+        |val c = (3
+        |        + 2)
+        """.trimMargin(),
+
+        """
+        |infix fun Int.combineWith(that: Int) = this + that
+        |
+        |fun f() {
+        |    val x = (1
+        |            combineWith 2
+        |            combineWith 3
+        |            combineWith 4
+        |            combineWith 5
+        |            combineWith 6)
+        |}
+        """.trimMargin(),
+
+        """
+        |fun f(i1: Int, i2: Int, i3: Int): Int {
+        |    if (i2 > 0
+        |            && i3 < 0) {
+        |        return 2
+        |    }
+        |    return 0
+        |}
+        """.trimMargin(),
+
+        """
+        |val value1 = (1
+        |        to 2
+        |        to 3)
+        """.trimMargin(),
+
+        """
+        |val value2 =
+        |    (1
+        |            to 2
+        |            to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value3 = identity(1
+        |        to 2
+        |        to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value4 = identity(
+        |    1
+        |            to 2
+        |            to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value5 =
+        |    identity(1
+        |            to 2
+        |            to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |/**
+        | * Line breaks:
+        | *
+        | * 1. before the expression body (`=`),
+        | * 2. before the effective function arguments, and
+        | * 3. on each infix function call ([to]).
+        | */
+        |val value6 =
+        |    identity(
+        |        1
+        |                to 2
+        |                to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value7 = identity(identity(1
+        |        to 2
+        |        to 3))
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value8 = identity(identity(
+        |    1
+        |            to 2
+        |            to 3))
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value9 =
+        |    identity(identity(1
+        |            to 2
+        |            to 3))
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |val value10 =
+        |    identity(identity(
+        |        1
+        |                to 2
+        |                to 3))
+        """.trimMargin(),
+
+        """
+        |// Same as above, but using a custom getter instead of an explicit initializer.
+        |val value11
+        |    get() =
+        |        (1
+        |                to 2
+        |                to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |// Same as above, but using a custom getter instead of an explicit initializer.
+        |val value12
+        |    get() =
+        |        identity(1
+        |                to 2
+        |                to 3)
+        """.trimMargin(),
+
+        """
+        |fun <T : Any> identity(t: T): T = t
+        |
+        |// Same as above, but using a custom getter instead of an explicit initializer.
+        |val value13
+        |    get() =
+        |        identity(identity(1
+        |                to 2
+        |                to 3))
+        """.trimMargin(),
+
+        """
+        |fun f() {
+        |    g(42
+        |            as Integer)
+        |}
+        """.trimMargin(),
+
+        """
+        |fun f() {
+        |    g(""
+        |            as? String?)
+        |}
+        """.trimMargin(),
+
+        """
+        |fun f() {
+        |    // The dot-qualified expression is always single-indented. 
+        |    ""
+        |        .length
+        |            as Int
+        |}
+        """.trimMargin(),
+    )
+
+    /**
+     * Expressions wrapped before an operator or an infix function.
+     *
+     * See [#1340](https://github.com/saveourtool/diktat/issues/1340).
+     */
+    val expressionsWrappedBeforeOperator = mapOf(
+        false to expressionsWrappedBeforeOperatorSingleIndent,
+        true to expressionsWrappedBeforeOperatorContinuationIndent)
+
     /**
      * Parenthesized expressions, `extendedIndentForExpressionBodies` is **off**,
      * `extendedIndentAfterOperators` is **on**.
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleWarnTest.kt
index 4de250a065..00dedf277c 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleWarnTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleWarnTest.kt
@@ -11,6 +11,7 @@ import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestMixin.withCust
 import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.dotQualifiedExpressions
 import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.expressionBodyFunctions
 import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.expressionsWrappedAfterOperator
+import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.expressionsWrappedBeforeOperator
 import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.ifExpressions
 import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationRuleTestResources.parenthesesSurroundedInfixExpressions
 import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_INDENTATION
@@ -329,8 +330,8 @@ class IndentationRuleWarnTest : LintTestBase(::IndentationRule) {
                     |
                     |    bar
                     |        .baz()
-                    |        as Baz
-                    |        as? Baz
+                    |            as Baz
+                    |            as? Baz
                     |}
                     |
             """.trimMargin()
@@ -909,6 +910,48 @@ class IndentationRuleWarnTest : LintTestBase(::IndentationRule) {
         }
     }
 
+    /**
+     * See [#1340](https://github.com/saveourtool/diktat/issues/1340).
+     */
+    @Nested
+    @TestMethodOrder(DisplayName::class)
+    inner class `Expressions wrapped before operator` {
+        @ParameterizedTest(name = "$EXTENDED_INDENT_AFTER_OPERATORS = {0}")
+        @ValueSource(booleans = [false, true])
+        @Tag(WarningNames.WRONG_INDENTATION)
+        fun `should be properly indented`(extendedIndentAfterOperators: Boolean) {
+            val defaultConfig = IndentationConfig(NEWLINE_AT_END to false)
+            val customConfig = defaultConfig.withCustomParameters(EXTENDED_INDENT_AFTER_OPERATORS to extendedIndentAfterOperators)
+
+            lintMultipleMethods(
+                expressionsWrappedBeforeOperator[extendedIndentAfterOperators].assertNotNull(),
+                lintErrors = emptyArray(),
+                customConfig.asRulesConfigList()
+            )
+        }
+
+        @ParameterizedTest(name = "$EXTENDED_INDENT_AFTER_OPERATORS = {0}")
+        @ValueSource(booleans = [false, true])
+        @Tag(WarningNames.WRONG_INDENTATION)
+        fun `should be reported if mis-indented`(extendedIndentAfterOperators: Boolean) {
+            val defaultConfig = IndentationConfig(NEWLINE_AT_END to false)
+            val customConfig = defaultConfig.withCustomParameters(EXTENDED_INDENT_AFTER_OPERATORS to extendedIndentAfterOperators)
+
+            assertSoftly { softly ->
+                expressionsWrappedBeforeOperator[!extendedIndentAfterOperators].assertNotNull().forEach { code ->
+                    softly.assertThat(lintResult(code, customConfig.asRulesConfigList()))
+                        .describedAs("lint result for ${code.describe()}")
+                        .isNotEmpty
+                        .hasSizeBetween(1, 5).allSatisfy(Consumer { lintError ->
+                            assertThat(lintError.ruleId).describedAs("ruleId").isEqualTo(ruleId)
+                            assertThat(lintError.canBeAutoCorrected).describedAs("canBeAutoCorrected").isTrue
+                            assertThat(lintError.detail).matches(warnTextRegex)
+                        })
+                }
+            }
+        }
+    }
+
     /**
      * See [#1409](https://github.com/saveourtool/diktat/issues/1409).
      */