Repository: analysis-dev/diktat (kotlin)

Issue:
`ANNOTATION_NEW_LINE` doesn't trigger if the annotation doesn't start on a new line
```
package example; @Annotation

class A { }
```

Pull request:
### What's done:
    Fixed bug
closes #657

Patch:
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt
index 8cc0c7be50..b337dd9520 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt
@@ -48,16 +48,14 @@ class AnnotationNewLineRule(private val configRules: List<RulesConfig>) : Rule("
         }
 
         node.getAllChildrenWithType(ANNOTATION_ENTRY).forEach {
-            if (!it.isFollowedByNewlineWithComment() || !it.isBeginByNewline()) {
-                deleteSpaces(it, !it.isFollowedByNewlineWithComment(), !it.isBeginByNewline())
+            if (!it.isFollowedByNewlineWithComment() || !it.isBeginNewLineWithComment()) {
+                deleteSpaces(it, !it.isFollowedByNewlineWithComment())
             }
         }
     }
 
-    // fixme added handle for left side!
     private fun deleteSpaces(node: ASTNode,
-                             rightSide: Boolean,
-                             leftSide: Boolean) {
+                             rightSide: Boolean) {
         Warnings.ANNOTATION_NEW_LINE.warnAndFix(configRules, emitWarn, isFixMode, "${node.text} not on a single line",
             node.startOffset, node) {
             if (rightSide) {
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 4876094202..e89802cd7a 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
@@ -24,13 +24,14 @@ import com.pinterest.ktlint.core.ast.ElementType.LBRACE
 import com.pinterest.ktlint.core.ast.ElementType.MODIFIER_LIST
 import com.pinterest.ktlint.core.ast.ElementType.OPERATION_REFERENCE
 import com.pinterest.ktlint.core.ast.ElementType.OVERRIDE_KEYWORD
-import com.pinterest.ktlint.core.ast.ElementType.PACKAGE_DIRECTIVE
 import com.pinterest.ktlint.core.ast.ElementType.PRIVATE_KEYWORD
 import com.pinterest.ktlint.core.ast.ElementType.PROTECTED_KEYWORD
 import com.pinterest.ktlint.core.ast.ElementType.PUBLIC_KEYWORD
 import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
 import com.pinterest.ktlint.core.ast.isLeaf
+import com.pinterest.ktlint.core.ast.isPartOfComment
 import com.pinterest.ktlint.core.ast.isRoot
+import com.pinterest.ktlint.core.ast.isWhiteSpace
 import com.pinterest.ktlint.core.ast.lineNumber
 import com.pinterest.ktlint.core.ast.parent
 import org.jetbrains.kotlin.com.intellij.lang.ASTNode
@@ -169,6 +170,14 @@ fun ASTNode.isBeginByNewline() =
                     (it.treePrev.elementType == IMPORT_LIST && it.treePrev.isLeaf() && it.treePrev.treePrev.isLeaf())
         } ?: false
 
+/**
+ * Checks if there is a newline before this element or before comment before. See [isBeginByNewline] for motivation on parents check.
+ */
+fun ASTNode.isBeginNewLineWithComment() =
+        isBeginByNewline() || siblings(forward = false).takeWhile { !it.textContains('\n') }.toList().run {
+            all { it.isWhiteSpace() || it.isPartOfComment() } && isNotEmpty()
+        }
+
 /**
  * checks if the node has corresponding child with elementTyp
  */


Test patch:
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/AnnotationNewLineRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/AnnotationNewLineRuleWarnTest.kt
index 83effae5eb..c4662d70c6 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/AnnotationNewLineRuleWarnTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/AnnotationNewLineRuleWarnTest.kt
@@ -281,27 +281,33 @@ class AnnotationNewLineRuleWarnTest : LintTestBase(::AnnotationNewLineRule) {
 
     @Test
     @Tag(WarningNames.ANNOTATION_NEW_LINE)
-    fun `should fix annotation on same line with import`() {
+    fun `should warn annotation for several annotations`() {
         lintMethod(
             """
-                    |import qwe.qwe;@ExperimentalStdlibApi
-                    |   @Hello
-                    |   override fun checkNode() {}
-                """.trimMargin(),
-            LintError(1, 16, ruleId, "${Warnings.ANNOTATION_NEW_LINE.warnText()} @ExperimentalStdlibApi not on a single line", true),
-        )
-    }
-
-    @Test
-    @Tag(WarningNames.ANNOTATION_NEW_LINE)
-    fun `should fix annotation on same line with package`() {
-        lintMethod(
-            """
-                    |package dfgh.dfgh;@ExperimentalStdlibApi
-                    |   @Hello
-                    |   override fun checkNode() {}
+                    |@ExperimentalStdlibApi /*   */ @Hello
+                    |override fun checkNode() {}
+                    |
+                    |/*    */ @Goo
+                    |class A {}
+                    |
+                    |@A1
+                    |/*   */ @A2
+                    |@A3
+                    |class A {}
+                    |
+                    |
+                    |@Foo class Foo {}
+                    |
+                    |@Foo
+                    |class Foo {}
+                    |
+                    |@Foo @Goo val loader: DataLoader
+                    |
+                    |@Foo
+                    |@goo val loader: DataLoader
                 """.trimMargin(),
-            LintError(1, 19, ruleId, "${Warnings.ANNOTATION_NEW_LINE.warnText()} @ExperimentalStdlibApi not on a single line", true),
+            LintError(1, 1, ruleId, "${Warnings.ANNOTATION_NEW_LINE.warnText()} @ExperimentalStdlibApi not on a single line", true),
+            LintError(1, 32, ruleId, "${Warnings.ANNOTATION_NEW_LINE.warnText()} @Hello not on a single line", true),
         )
     }
 }
diff --git a/diktat-rules/src/test/resources/test/paragraph3/annotations/AnnotationCommentExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/annotations/AnnotationCommentExpected.kt
index 98feb967a1..ef72b3a608 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/annotations/AnnotationCommentExpected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/annotations/AnnotationCommentExpected.kt
@@ -1,4 +1,11 @@
 @Hello() // hello
 @Hi
 class A {
-}
\ No newline at end of file
+}
+@Foo
+/*  */ @Goo
+/*  */ @Qwe
+ class AA {}
+/**  */ @Foo
+/** */ @Gg
+ class Bb {}
diff --git a/diktat-rules/src/test/resources/test/paragraph3/annotations/AnnotationCommentTest.kt b/diktat-rules/src/test/resources/test/paragraph3/annotations/AnnotationCommentTest.kt
index 98feb967a1..f6b5e30cfb 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/annotations/AnnotationCommentTest.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/annotations/AnnotationCommentTest.kt
@@ -1,4 +1,6 @@
 @Hello() // hello
 @Hi
 class A {
-}
\ No newline at end of file
+}
+@Foo /*  */ @Goo /*  */ @Qwe class AA {}
+/**  */ @Foo /** */ @Gg class Bb {}
\ No newline at end of file