Repository: analysis-dev/diktat (kotlin)

Issue:
`WRONG_INDENTATION` false-positive: "the same number of indents to the opening and closing quotes was expected"
## Describe the bug
I believe, caused by https://github.com/saveourtool/diktat/commit/998d0e97bf838eec12f7852568b5d4932401e707

## Observed behavior
https://github.com/saveourtool/save-cli/security/code-scanning/132
https://github.com/saveourtool/save-cli/security/code-scanning/131
Reproducible on diktat-demo with this snippet:
```kotlin
package com.saveourtool.save.reporter.plain

open class PlainTextReporter {
    @Suppress("MAGIC_NUMBER", "MagicNumber")
    override fun afterAll() {
        out.write(
            "$status: $total tests, $passRate%% successful, failed: $failed, skipped: $skipped"
                .encodeToByteArray()
            )
    }
}
```

## Environment information
* diktat version: 1.2.3

Pull request:
### What's done:

 * Fixes the false-positive warnings reported for single-line string templates.
 * Closes #1490.
 * See #811 (the original feature request).
 * See #1364 (the pull request which introduced the regression).
 * See 998d0e9 (the commit which introduced the regression).

Patch:
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt
index ce1a828f65..d53f145e09 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/IndentationRule.kt
@@ -2,6 +2,8 @@
  * Main logic of indentation including Rule and utility classes and methods.
  */
 
+@file:Suppress("FILE_UNORDERED_IMPORTS")// False positives, see #1494.
+
 package org.cqfn.diktat.ruleset.rules.chapter3.files
 
 import org.cqfn.diktat.common.config.rules.RulesConfig
@@ -79,11 +81,13 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
 import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
 import org.jetbrains.kotlin.psi.psiUtil.startOffset
 
-import java.util.ArrayDeque as Stack
-
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.contract
 import kotlin.math.abs
 import kotlin.reflect.KCallable
 
+import java.util.ArrayDeque as Stack
+
 /**
  * Rule that checks indentation. The following general rules are checked:
  * 1. Only spaces should be used each indentation is equal to 4 spaces
@@ -198,21 +202,6 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
             }
         }
 
-    private fun isCloseAndOpenQuoterOffset(nodeWhiteSpace: ASTNode, expectedIndent: Int): Boolean {
-        val nextNode = nodeWhiteSpace.treeNext
-        if (nextNode.elementType == VALUE_ARGUMENT) {
-            val nextNodeDot = getNextDotExpression(nextNode)
-            nextNodeDot?.getFirstChildWithType(STRING_TEMPLATE)?.let {
-                if (it.getAllChildrenWithType(LITERAL_STRING_TEMPLATE_ENTRY).size > 1) {
-                    val closingQuote = it.getFirstChildWithType(CLOSING_QUOTE)?.treePrev?.text
-                        ?.length ?: -1
-                    return expectedIndent == closingQuote
-                }
-            }
-        }
-        return true
-    }
-
     @Suppress("ForbiddenComment")
     private fun IndentContext.visitWhiteSpace(astNode: ASTNode) {
         require(astNode.isMultilineWhitespace()) {
@@ -242,10 +231,10 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
             addException(astNode.treeParent, abs(indentError.expected - indentError.actual), false)
         }
 
-        val difOffsetCloseAndOpenQuote = isCloseAndOpenQuoterOffset(astNode, indentError.actual)
+        val alignedOpeningAndClosingQuotes = hasAlignedOpeningAndClosingQuotes(astNode, indentError.actual)
 
-        if ((checkResult?.isCorrect != true && expectedIndent != indentError.actual) || !difOffsetCloseAndOpenQuote) {
-            val warnText = if (!difOffsetCloseAndOpenQuote) {
+        if ((checkResult?.isCorrect != true && expectedIndent != indentError.actual) || !alignedOpeningAndClosingQuotes) {
+            val warnText = if (!alignedOpeningAndClosingQuotes) {
                 "the same number of indents to the opening and closing quotes was expected"
             } else {
                 "expected $expectedIndent but was ${indentError.actual}"
@@ -268,7 +257,7 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
         expectedIndent: Int,
         actualIndent: Int
     ) {
-        val nextNodeDot = getNextDotExpression(whiteSpace.node.treeNext)
+        val nextNodeDot = whiteSpace.node.treeNext.getNextDotExpression()
         if (nextNodeDot != null &&
                 nextNodeDot.elementType == DOT_QUALIFIED_EXPRESSION &&
                 nextNodeDot.firstChildNode.elementType == STRING_TEMPLATE &&
@@ -361,12 +350,6 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
         }
     }
 
-    private fun getNextDotExpression(node: ASTNode) = if (node.elementType == DOT_QUALIFIED_EXPRESSION) {
-        node
-    } else {
-        node.getFirstChildWithType(DOT_QUALIFIED_EXPRESSION)
-    }
-
     /**
      * Modifies [templateEntry] by correcting its indentation level.
      *
@@ -734,11 +717,30 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
         private fun ASTNode.isMultilineWhitespace(): Boolean =
             elementType == WHITE_SPACE && textContains(NEWLINE)
 
+        @OptIn(ExperimentalContracts::class)
+        private fun ASTNode?.isMultilineStringTemplate(): Boolean {
+            contract {
+                returns(true) implies (this@isMultilineStringTemplate != null)
+            }
+
+            this ?: return false
+
+            return elementType == STRING_TEMPLATE &&
+                    getAllChildrenWithType(LITERAL_STRING_TEMPLATE_ENTRY).any { entry ->
+                        entry.textContains(NEWLINE)
+                    }
+        }
+
         /**
          * @return `true` if this is a [String.trimIndent] or [String.trimMargin]
          * call, `false` otherwise.
          */
+        @OptIn(ExperimentalContracts::class)
         private fun ASTNode?.isTrimIndentOrMarginCall(): Boolean {
+            contract {
+                returns(true) implies (this@isTrimIndentOrMarginCall != null)
+            }
+
             this ?: return false
 
             require(elementType == CALL_EXPRESSION) {
@@ -758,6 +760,12 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
             return identifier.text in knownTrimFunctionPatterns
         }
 
+        private fun ASTNode.getNextDotExpression(): ASTNode? =
+            when (elementType) {
+                DOT_QUALIFIED_EXPRESSION -> this
+                else -> getFirstChildWithType(DOT_QUALIFIED_EXPRESSION)
+            }
+
         /**
          * @return the matching closing brace type for this opening brace type,
          *   or vice versa.
@@ -791,5 +799,64 @@ class IndentationRule(configRules: List<RulesConfig>) : DiktatRule(
                 this > 0 -> this
                 else -> 0
             }
+
+        /**
+         * Processes fragments like:
+         *
+         * ```kotlin
+         * f(
+         *     """
+         *     |foobar
+         *     """.trimMargin()
+         * )
+         * ```
+         *
+         * @param whitespace the whitespace node between an [LPAR] and the
+         *   `trimIndent()`- or `trimMargin()`- terminated string template, which is
+         *   an effective argument of a function call. The string template is
+         *   expected to begin on a separate line (otherwise, there'll be no
+         *   whitespace in-between).
+         * @return `true` if the opening and the closing quotes of the string
+         *   template are aligned, `false` otherwise.
+         */
+        private fun hasAlignedOpeningAndClosingQuotes(whitespace: ASTNode, expectedIndent: Int): Boolean {
+            require(whitespace.isMultilineWhitespace()) {
+                "The node is $whitespace while a multi-line $WHITE_SPACE expected"
+            }
+
+            /*
+             * Here, we expect that `nextNode` is a VALUE_ARGUMENT which contains
+             * the dot-qualified expression (`STRING_TEMPLATE.trimIndent()` or
+             * `STRING_TEMPLATE.trimMargin()`).
+             */
+            val nextFunctionArgument = whitespace.treeNext
+            if (nextFunctionArgument.elementType == VALUE_ARGUMENT) {
+                val memberOrExtensionCall = nextFunctionArgument.getNextDotExpression()
+
+                /*
+                 * Limit allowed member or extension calls to `trimIndent()` and
+                 * `trimMargin()`.
+                 */
+                if (memberOrExtensionCall != null &&
+                        memberOrExtensionCall.getFirstChildWithType(CALL_EXPRESSION).isTrimIndentOrMarginCall()) {
+                    val stringTemplate = memberOrExtensionCall.getFirstChildWithType(STRING_TEMPLATE)
+
+                    /*
+                     * Limit the logic to multi-line string templates only (the
+                     * opening and closing quotes of a single-line template are,
+                     * obviously, always mis-aligned).
+                     */
+                    if (stringTemplate != null && stringTemplate.isMultilineStringTemplate()) {
+                        val closingQuoteIndent = stringTemplate.getFirstChildWithType(CLOSING_QUOTE)
+                            ?.treePrev
+                            ?.text
+                            ?.length ?: -1
+                        return expectedIndent == closingQuoteIndent
+                    }
+                }
+            }
+
+            return true
+        }
     }
 }


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 b6a24f9204..75cfdd41be 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
@@ -1,3 +1,5 @@
+@file:Suppress("FILE_UNORDERED_IMPORTS")// False positives, see #1494.
+
 package org.cqfn.diktat.ruleset.chapter3.spaces
 
 import org.cqfn.diktat.common.config.rules.RulesConfig
@@ -10,12 +12,20 @@ import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXT
 import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXTENDED_INDENT_FOR_EXPRESSION_BODIES
 import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXTENDED_INDENT_OF_PARAMETERS
 import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig.Companion.NEWLINE_AT_END
+import org.cqfn.diktat.test.framework.processing.FileComparisonResult
 import org.cqfn.diktat.util.FixTestBase
 
 import generated.WarningNames
+import org.assertj.core.api.Assertions.assertThat
+import org.intellij.lang.annotations.Language
+import org.junit.jupiter.api.Nested
 import org.junit.jupiter.api.Tag
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.TestMethodOrder
+import org.junit.jupiter.api.io.TempDir
+import java.nio.file.Path
+
+import org.cqfn.diktat.ruleset.chapter3.spaces.IndentationConfigFactory as IndentationConfig
 
 /**
  * Legacy indentation tests.
@@ -64,9 +74,167 @@ class IndentationRuleFixTest : FixTestBase("test/paragraph3/indentation",
         fixAndCompare("ConstructorExpected.kt", "ConstructorTest.kt")
     }
 
-    @Test
-    @Tag(WarningNames.WRONG_INDENTATION)
-    fun `multiline string`() {
-        fixAndCompare("MultilionStringExpected.kt", "MultilionStringTest.kt")
+    @Nested
+    @TestMethodOrder(NaturalDisplayName::class)
+    inner class `Multi-line string literals` {
+        /**
+         * Correctly-indented opening quotation mark, incorrectly-indented
+         * closing quotation mark.
+         */
+        @Test
+        @Tag(WarningNames.WRONG_INDENTATION)
+        @Suppress("LOCAL_VARIABLE_EARLY_DECLARATION")  // False positives
+        fun `case 1 - mis-aligned opening and closing quotes`(@TempDir tempDir: Path) {
+            val actualCode = """
+            |fun f() {
+            |    g(
+            |        ""${'"'}
+            |            |val q = 1
+            |            |
+            |                    ""${'"'}.trimMargin(),
+            |        arg1 = "arg1"
+            |    )
+            |}
+            """.trimMargin()
+
+            val expectedCode = """
+            |fun f() {
+            |    g(
+            |        ""${'"'}
+            |            |val q = 1
+            |            |
+            |        ""${'"'}.trimMargin(),
+            |        arg1 = "arg1"
+            |    )
+            |}
+            """.trimMargin()
+
+            val lintResult = fixAndCompareContent(actualCode, expectedCode, tempDir)
+            assertThat(lintResult.actualContent)
+                .describedAs("lint result for ${actualCode.describe()}")
+                .isEqualTo(lintResult.expectedContent)
+        }
+
+        /**
+         * Both the opening and the closing quotation marks are incorrectly
+         * indented (indentation level is less than needed).
+         */
+        @Test
+        @Tag(WarningNames.WRONG_INDENTATION)
+        @Suppress("LOCAL_VARIABLE_EARLY_DECLARATION")  // False positives
+        fun `case 2`(@TempDir tempDir: Path) {
+            val actualCode = """
+            |fun f() {
+            |    g(
+            |    ""${'"'}
+            |            |val q = 1
+            |            |
+            |    ""${'"'}.trimMargin(),
+            |        arg1 = "arg1"
+            |    )
+            |}
+            """.trimMargin()
+
+            val expectedCode = """
+            |fun f() {
+            |    g(
+            |        ""${'"'}
+            |                |val q = 1
+            |                |
+            |        ""${'"'}.trimMargin(),
+            |        arg1 = "arg1"
+            |    )
+            |}
+            """.trimMargin()
+
+            val lintResult = fixAndCompareContent(actualCode, expectedCode, tempDir)
+            assertThat(lintResult.actualContent)
+                .describedAs("lint result for ${actualCode.describe()}")
+                .isEqualTo(lintResult.expectedContent)
+        }
+
+        /**
+         * Both the opening and the closing quotation marks are incorrectly
+         * indented (indentation level is greater than needed).
+         */
+        @Test
+        @Tag(WarningNames.WRONG_INDENTATION)
+        @Suppress("LOCAL_VARIABLE_EARLY_DECLARATION")  // False positives
+        fun `case 3`(@TempDir tempDir: Path) {
+            val actualCode = """
+            |fun f() {
+            |    g(
+            |            ""${'"'}
+            |                    |val q = 1
+            |                    |
+            |            ""${'"'}.trimMargin(),
+            |        arg1 = "arg1"
+            |    )
+            |}
+            """.trimMargin()
+
+            val expectedCode = """
+            |fun f() {
+            |    g(
+            |        ""${'"'}
+            |                |val q = 1
+            |                |
+            |        ""${'"'}.trimMargin(),
+            |        arg1 = "arg1"
+            |    )
+            |}
+            """.trimMargin()
+
+            val lintResult = fixAndCompareContent(actualCode, expectedCode, tempDir)
+            assertThat(lintResult.actualContent)
+                .describedAs("lint result for ${actualCode.describe()}")
+                .isEqualTo(lintResult.expectedContent)
+        }
+
+        /**
+         * Both the opening and the closing quotation marks are incorrectly
+         * indented and misaligned.
+         */
+        @Test
+        @Tag(WarningNames.WRONG_INDENTATION)
+        @Suppress("LOCAL_VARIABLE_EARLY_DECLARATION")  // False positives
+        fun `case 4 - mis-aligned opening and closing quotes`(@TempDir tempDir: Path) {
+            val actualCode = """
+            |fun f() {
+            |    g(
+            |            ""${'"'}
+            |                    |val q = 1
+            |                    |
+            |                            ""${'"'}.trimMargin(),
+            |        arg1 = "arg1"
+            |    )
+            |}
+            """.trimMargin()
+
+            val expectedCode = """
+            |fun f() {
+            |    g(
+            |        ""${'"'}
+            |                |val q = 1
+            |                |
+            |        ""${'"'}.trimMargin(),
+            |        arg1 = "arg1"
+            |    )
+            |}
+            """.trimMargin()
+
+            val lintResult = fixAndCompareContent(actualCode, expectedCode, tempDir)
+            assertThat(lintResult.actualContent)
+                .describedAs("lint result for ${actualCode.describe()}")
+                .isEqualTo(lintResult.expectedContent)
+        }
+
+        private fun fixAndCompareContent(@Language("kotlin") actualCode: String,
+                                         @Language("kotlin") expectedCode: String,
+                                         tempDir: Path
+        ): FileComparisonResult {
+            val config = IndentationConfig(NEWLINE_AT_END to false).withCustomParameters().asRulesConfigList()
+            return fixAndCompareContent(actualCode, expectedCode, tempDir, config)
+        }
     }
 }
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleTest.kt
index 5c8fb9ec64..b7931600f0 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/IndentationRuleTest.kt
@@ -259,16 +259,64 @@ class IndentationRuleTest {
     @TestMethodOrder(NaturalDisplayName::class)
     inner class `String templates` {
         /**
+         * No message like
+         *
+         * > only spaces are allowed for indentation and each indentation should
+         * > equal to 4 spaces (tabs are not allowed): the same number of
+         * > indents to the opening and closing quotes was expected
+         *
+         * should be reported.
+         *
+         * See [#1490](https://github.com/saveourtool/diktat/issues/1490).
+         */
+        @IndentationTest(IndentedSourceCode(
+            """
+            val value = f(
+                "text ${'$'}variable text".isEmpty()
+            )
+            """),
+            singleConfiguration = true)
+        fun `mis-aligned opening and closing quotes of a string template, false positive, case 1 (#1490)`() = Unit
+
+        /**
+         * No message like
+         *
+         * > only spaces are allowed for indentation and each indentation should
+         * > equal to 4 spaces (tabs are not allowed): the same number of
+         * > indents to the opening and closing quotes was expected
+         *
+         * should be reported.
+         *
+         * See [#1490](https://github.com/saveourtool/diktat/issues/1490).
+         */
+        @IndentationTest(IndentedSourceCode(
+            """
+            val value = f(
+                "text ${'$'}variable text".trimIndent()
+            )
+            """),
+            singleConfiguration = true)
+        fun `mis-aligned opening and closing quotes of a string template, false positive, case 2 (#1490)`() = Unit
+
+        /**
+         * No message like
+         *
+         * > only spaces are allowed for indentation and each indentation should
+         * > equal to 4 spaces (tabs are not allowed): the same number of
+         * > indents to the opening and closing quotes was expected
+         *
+         * should be reported.
+         *
          * See [#1490](https://github.com/saveourtool/diktat/issues/1490).
          */
         @IndentationTest(IndentedSourceCode(
             """
             val value = f(
-                "text ${'$'}variable text".isEmpty() // diktat:WRONG_INDENTATION[message = only spaces are allowed for indentation and each indentation should equal to 4 spaces (tabs are not allowed): the same number of indents to the opening and closing quotes was expected]
+                "text ${'$'}variable text".trimMargin()
             )
             """),
             singleConfiguration = true)
-        fun `issue #1490`() = Unit
+        fun `mis-aligned opening and closing quotes of a string template, false positive, case 3 (#1490)`() = Unit
     }
 
     /**
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/junit/IndentationTestInvocationContextProvider.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/junit/IndentationTestInvocationContextProvider.kt
index 30bf592b51..2bee3d4f0f 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/junit/IndentationTestInvocationContextProvider.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/junit/IndentationTestInvocationContextProvider.kt
@@ -2,6 +2,7 @@ package org.cqfn.diktat.ruleset.chapter3.spaces.junit
 
 import org.cqfn.diktat.ruleset.chapter3.spaces.ExpectedIndentationError
 import org.cqfn.diktat.ruleset.junit.RuleInvocationContextProvider
+import org.cqfn.diktat.ruleset.utils.NEWLINE
 import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXTENDED_INDENT_AFTER_OPERATORS
 import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXTENDED_INDENT_BEFORE_DOT
 import org.cqfn.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXTENDED_INDENT_FOR_EXPRESSION_BODIES
@@ -95,30 +96,39 @@ class IndentationTestInvocationContextProvider : RuleInvocationContextProvider<I
             contexts += IndentationTestWarnInvocationContext(customConfig0, actualCode = code0, expectedErrors)
         }
 
-        if (!singleConfiguration) {
-            val testInput1 = indentationTest.second.extractTestInput(
-                supportedTags,
-                allowEmptyErrors = !includeWarnTests)
-            val (code1, expectedErrors1, customConfig1) = testInput1
-
-            assertThat(code0)
-                .describedAs("Both code fragments are the same")
-                .isNotEqualTo(code1)
-            assertThat(customConfig0)
-                .describedAs("Both custom configs are the same")
-                .isNotEqualTo(customConfig1)
-            assertThat(testInput0.effectiveConfig)
-                .describedAs("Both effective configs are the same")
-                .isNotEqualTo(testInput1.effectiveConfig)
-
-            contexts += IndentationTestFixInvocationContext(customConfig1, actualCode = code1)
-            contexts += IndentationTestFixInvocationContext(customConfig1, actualCode = code0, expectedCode = code1)
-            contexts += IndentationTestFixInvocationContext(customConfig0, actualCode = code1, expectedCode = code0)
-
-            if (includeWarnTests) {
-                contexts += IndentationTestWarnInvocationContext(customConfig1, actualCode = code1)
-                contexts += IndentationTestWarnInvocationContext(customConfig1, actualCode = code0, expectedErrors0)
-                contexts += IndentationTestWarnInvocationContext(customConfig0, actualCode = code1, expectedErrors1)
+        when {
+            singleConfiguration -> {
+                val code1 = indentationTest.second.code
+                assertThat(code1)
+                    .describedAs("The 2nd code fragment should be empty if `singleConfiguration` is `true`: $NEWLINE$code1")
+                    .isEmpty()
+            }
+
+            else -> {
+                val testInput1 = indentationTest.second.extractTestInput(
+                    supportedTags,
+                    allowEmptyErrors = !includeWarnTests)
+                val (code1, expectedErrors1, customConfig1) = testInput1
+
+                assertThat(code0)
+                    .describedAs("Both code fragments are the same")
+                    .isNotEqualTo(code1)
+                assertThat(customConfig0)
+                    .describedAs("Both custom configs are the same")
+                    .isNotEqualTo(customConfig1)
+                assertThat(testInput0.effectiveConfig)
+                    .describedAs("Both effective configs are the same")
+                    .isNotEqualTo(testInput1.effectiveConfig)
+
+                contexts += IndentationTestFixInvocationContext(customConfig1, actualCode = code1)
+                contexts += IndentationTestFixInvocationContext(customConfig1, actualCode = code0, expectedCode = code1)
+                contexts += IndentationTestFixInvocationContext(customConfig0, actualCode = code1, expectedCode = code0)
+
+                if (includeWarnTests) {
+                    contexts += IndentationTestWarnInvocationContext(customConfig1, actualCode = code1)
+                    contexts += IndentationTestWarnInvocationContext(customConfig1, actualCode = code0, expectedErrors0)
+                    contexts += IndentationTestWarnInvocationContext(customConfig0, actualCode = code1, expectedErrors1)
+                }
             }
         }
 
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/junit/IndentedSourceCode.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/junit/IndentedSourceCode.kt
index 4c491dd82b..f18b413300 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/junit/IndentedSourceCode.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/spaces/junit/IndentedSourceCode.kt
@@ -24,6 +24,7 @@ import kotlin.annotation.AnnotationRetention.RUNTIME
  * @property extendedIndentAfterOperators describes the effective formatting of [code].
  * @property extendedIndentBeforeDot describes the effective formatting of [code].
  */
+@Target
 @Retention(RUNTIME)
 @MustBeDocumented
 annotation class IndentedSourceCode(
diff --git a/diktat-rules/src/test/resources/test/paragraph3/indentation/MultilionStringExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/indentation/MultilionStringExpected.kt
deleted file mode 100644
index d4007dd98c..0000000000
--- a/diktat-rules/src/test/resources/test/paragraph3/indentation/MultilionStringExpected.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package test.paragraph3.indentation
-
-//test of correct opening quotation mark and incorrect closing quotation mark
-fun multilionString() {
-    lintMethod(
-            """
-                |val q = 1
-                |
-            """.trimMargin(),
-            fileName = "src/main/kotlin/org/cqfn/diktat/Example.kts"
-    )
-}
-
-//test of incorrect opening quotation mark and incorrect closing quotation mark1
-fun multilionString() {
-    lintMethod(
-            """
-                    |val q = 1
-                    |
-            """.trimMargin(),
-            fileName = "src/main/kotlin/org/cqfn/diktat/Example.kts"
-    )
-}
-
-//test of incorrect opening quotation mark and incorrect closing quotation mark2
-fun multilionString() {
-    lintMethod(
-            """
-                    |val q = 1
-                    |
-            """.trimMargin(),
-            fileName = "src/main/kotlin/org/cqfn/diktat/Example.kts"
-    )
-}
-
-//test of incorrect opening quotation mark and incorrect closing quotation mark with incorrect shift
-fun multilionString() {
-    lintMethod(
-            """
-                    |val q = 1
-                    |
-            """.trimMargin(),
-            fileName = "src/main/kotlin/org/cqfn/diktat/Example.kts"
-    )
-}
-
diff --git a/diktat-rules/src/test/resources/test/paragraph3/indentation/MultilionStringTest.kt b/diktat-rules/src/test/resources/test/paragraph3/indentation/MultilionStringTest.kt
deleted file mode 100644
index 6274177be1..0000000000
--- a/diktat-rules/src/test/resources/test/paragraph3/indentation/MultilionStringTest.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package test.paragraph3.indentation
-
-//test of correct opening quotation mark and incorrect closing quotation mark
-fun multilionString() {
-    lintMethod(
-            """
-                |val q = 1
-                |
-                        """.trimMargin(),
-            fileName = "src/main/kotlin/org/cqfn/diktat/Example.kts"
-    )
-}
-
-//test of incorrect opening quotation mark and incorrect closing quotation mark1
-fun multilionString() {
-    lintMethod(
-    """
-            |val q = 1
-            |
-    """.trimMargin(),
-        fileName = "src/main/kotlin/org/cqfn/diktat/Example.kts"
-    )
-}
-
-//test of incorrect opening quotation mark and incorrect closing quotation mark2
-fun multilionString() {
-    lintMethod(
-                """
-                        |val q = 1
-                        |
-                """.trimMargin(),
-        fileName = "src/main/kotlin/org/cqfn/diktat/Example.kts"
-    )
-}
-
-//test of incorrect opening quotation mark and incorrect closing quotation mark with incorrect shift
-fun multilionString() {
-    lintMethod(
-                """
-                        |val q = 1
-                        |
-                                """.trimMargin(),
-                fileName = "src/main/kotlin/org/cqfn/diktat/Example.kts"
-    )
-}
-