Repository: cqfn/diKTat (kotlin)

Issue:
FP for gradle scripts
## Describe the bug
```kotlin
// build.gradle.kts
version = "0.1.0-SNAPSHOT"

// settings.gradle.kts
rootProject.name = "save"
```
should be allowed, but diktat suggests to wrap them in `run`.

 - `HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE` shouldn't trigger for gradle scripts (should for regular kts files)

## Environment information
* diktat version: 0.5.0

Pull request:
### What's done:
- Fixed logic in `RunScriptRule`
- Removed handler for header in gradle script
- Closes #834

Patch:
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/comments/HeaderCommentRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/comments/HeaderCommentRule.kt
index 4202bb7d1f..421cbff623 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/comments/HeaderCommentRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter2/comments/HeaderCommentRule.kt
@@ -13,7 +13,10 @@ import org.cqfn.diktat.ruleset.utils.copyrightWords
 import org.cqfn.diktat.ruleset.utils.findChildAfter
 import org.cqfn.diktat.ruleset.utils.findChildBefore
 import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType
+import org.cqfn.diktat.ruleset.utils.getFilePath
 import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
+import org.cqfn.diktat.ruleset.utils.getRootNode
+import org.cqfn.diktat.ruleset.utils.isGradleScript
 import org.cqfn.diktat.ruleset.utils.moveChildBefore
 
 import com.pinterest.ktlint.core.ast.ElementType
@@ -45,7 +48,7 @@ class HeaderCommentRule(configRules: List<RulesConfig>) : DiktatRule(
     listOf(HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE, HEADER_MISSING_OR_WRONG_COPYRIGHT, HEADER_NOT_BEFORE_PACKAGE,
         HEADER_NOT_BEFORE_PACKAGE, HEADER_WRONG_FORMAT, WRONG_COPYRIGHT_YEAR)) {
     override fun logic(node: ASTNode) {
-        if (node.elementType == FILE) {
+        if (node.elementType == FILE && !node.getRootNode().getFilePath().isGradleScript()) {
             checkCopyright(node)
             if (checkHeaderKdocPosition(node)) {
                 checkHeaderKdoc(node)
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/RunInScript.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/RunInScript.kt
index ab48e27c1b..70d41d663c 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/RunInScript.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter6/RunInScript.kt
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement
 /**
  * Rule that checks if kts script contains other functions except run code
  * In .kts files allow use only property declaration, function, classes, and code inside `run` block
- * In gradle.kts files allow to call expression and dot qualified expression in addition to everything used in .kts files
+ * In gradle.kts files allow to call binary expression with EQ, expression and dot qualified expression in addition to everything used in .kts files
  */
 class RunInScript(private val configRules: List<RulesConfig>) : Rule("run-script") {
     private var isFixMode: Boolean = false
@@ -44,20 +44,16 @@ class RunInScript(private val configRules: List<RulesConfig>) : Rule("run-script
     }
 
     private fun checkGradleNode(node: ASTNode) {
-        val astNode = if (node.firstChildNode.elementType == PARENTHESIZED) {
-            node.firstChildNode
+        val astNode = if (node.hasEqBinaryExpression()) {
+            return
         } else {
-            node
+            when (node.firstChildNode.elementType) {
+                PARENTHESIZED -> node.firstChildNode
+                else -> node
+            }
         }
         if (!astNode.hasChildOfType(CALL_EXPRESSION) && !astNode.hasChildOfType(DOT_QUALIFIED_EXPRESSION)) {
-            RUN_IN_SCRIPT.warnAndFix(configRules, emitWarn, isFixMode, astNode.text, astNode.startOffset, astNode) {
-                val parent = astNode.treeParent
-                val newNode = KotlinParser().createNode("run {\n ${astNode.text}\n} \n")
-                val newScript = CompositeElement(SCRIPT_INITIALIZER)
-                parent.addChild(newScript, astNode)
-                newScript.addChild(newNode)
-                parent.removeChild(astNode)
-            }
+            warnRunInScript(astNode)
         }
     }
 
@@ -65,15 +61,19 @@ class RunInScript(private val configRules: List<RulesConfig>) : Rule("run-script
         val isLambdaArgument = node.firstChildNode.hasChildOfType(LAMBDA_ARGUMENT)
         val isLambdaInsideValueArgument = node.firstChildNode.findChildByType(VALUE_ARGUMENT_LIST)?.findChildByType(VALUE_ARGUMENT)?.findChildByType(LAMBDA_EXPRESSION) != null
         if (!(isLambdaArgument || isLambdaInsideValueArgument)) {
-            RUN_IN_SCRIPT.warnAndFix(configRules, emitWarn, isFixMode, node.text, node.startOffset, node) {
-                if (node.firstChildNode.elementType != DOT_QUALIFIED_EXPRESSION) {
-                    val parent = node.treeParent
-                    val newNode = KotlinParser().createNode("run {\n ${node.text}\n} \n")
-                    val newScript = CompositeElement(SCRIPT_INITIALIZER)
-                    parent.addChild(newScript, node)
-                    newScript.addChild(newNode)
-                    parent.removeChild(node)
-                }
+            warnRunInScript(node)
+        }
+    }
+
+    private fun warnRunInScript(node: ASTNode) {
+        RUN_IN_SCRIPT.warnAndFix(configRules, emitWarn, isFixMode, node.text, node.startOffset, node) {
+            if (node.firstChildNode.elementType != DOT_QUALIFIED_EXPRESSION) {
+                val parent = node.treeParent
+                val newNode = KotlinParser().createNode("run {\n ${node.text}\n} \n")
+                val newScript = CompositeElement(SCRIPT_INITIALIZER)
+                parent.addChild(newScript, node)
+                newScript.addChild(newNode)
+                parent.removeChild(node)
             }
         }
     }
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 2feed40a6f..d9f77e422c 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
@@ -17,9 +17,11 @@ import com.pinterest.ktlint.core.KtLint
 import com.pinterest.ktlint.core.ast.ElementType
 import com.pinterest.ktlint.core.ast.ElementType.ANNOTATED_EXPRESSION
 import com.pinterest.ktlint.core.ast.ElementType.ANNOTATION_ENTRY
+import com.pinterest.ktlint.core.ast.ElementType.BINARY_EXPRESSION
 import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT
 import com.pinterest.ktlint.core.ast.ElementType.CONST_KEYWORD
 import com.pinterest.ktlint.core.ast.ElementType.EOL_COMMENT
+import com.pinterest.ktlint.core.ast.ElementType.EQ
 import com.pinterest.ktlint.core.ast.ElementType.FILE
 import com.pinterest.ktlint.core.ast.ElementType.FILE_ANNOTATION_LIST
 import com.pinterest.ktlint.core.ast.ElementType.IMPORT_LIST
@@ -28,6 +30,7 @@ import com.pinterest.ktlint.core.ast.ElementType.KDOC
 import com.pinterest.ktlint.core.ast.ElementType.LATEINIT_KEYWORD
 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.PRIVATE_KEYWORD
 import com.pinterest.ktlint.core.ast.ElementType.PROPERTY
@@ -750,6 +753,15 @@ fun ASTNode.isGoingAfter(otherNode: ASTNode): Boolean {
     return (thisLineNumber > otherLineNumber)
 }
 
+/**
+ * check that node has binary expression with `EQ`
+ */
+fun ASTNode.hasEqBinaryExpression(): Boolean =
+        findChildByType(BINARY_EXPRESSION)
+            ?.findChildByType(OPERATION_REFERENCE)
+            ?.hasChildOfType(EQ)
+            ?: false
+
 /**
  * Get line number, where this node's content starts. To avoid `ArrayIndexOutOfBoundsException`s we check whether node's maximum offset is less than
  * Document's maximum offset, and calculate line number manually if needed.


Test patch:
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt
index f7774578ee..fb91c8ea2c 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt
@@ -373,4 +373,31 @@ class HeaderCommentRuleTest : LintTestBase(::HeaderCommentRule) {
             rulesConfigList = rulesConfigList
         )
     }
+
+    @Test
+    @Tag(WarningNames.HEADER_NOT_BEFORE_PACKAGE)
+    fun `header KDoc in gradle script`() {
+        lintMethod(
+            """                
+                |version = "0.1.0-SNAPSHOT"
+                |
+            """.trimMargin(),
+            fileName = "src/main/kotlin/org/cqfn/diktat/builds.gradle.kts"
+
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.HEADER_NOT_BEFORE_PACKAGE)
+    fun `header KDoc in kts script`() {
+        lintMethod(
+            """                
+                |val version = "0.1.0-SNAPSHOT"
+                |
+            """.trimMargin(),
+            LintError(1, 1, ruleId, "${HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE.warnText()} there are 0 declared classes and/or objects"),
+            fileName = "src/main/kotlin/org/cqfn/diktat/Example.kts"
+
+        )
+    }
 }
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/RunInScriptWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/RunInScriptWarnTest.kt
index 8deb1d9ac1..6618fab740 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/RunInScriptWarnTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/RunInScriptWarnTest.kt
@@ -130,4 +130,42 @@ class RunInScriptWarnTest : LintTestBase(::RunInScript) {
             fileName = "src/main/kotlin/org/cqfn/diktat/builds.gradle.kts"
         )
     }
+
+    @Test
+    @Tag(WarningNames.RUN_IN_SCRIPT)
+    fun `check gradle script with eq expression`() {
+        lintMethod(
+            """
+                version = "0.1.0-SNAPSHOT"
+                
+                diktat {}
+                
+                diktat({})
+
+                foo/*df*/()
+                
+                foo().goo()
+            """.trimMargin(),
+            fileName = "src/main/kotlin/org/cqfn/diktat/builds.gradle.kts"
+        )
+    }
+
+    @Test
+    @Tag(WarningNames.RUN_IN_SCRIPT)
+    fun `check kts script with eq expression`() {
+        lintMethod(
+            """
+                version = "0.1.0-SNAPSHOT"
+                
+                diktat {}
+                
+                diktat({})
+
+                foo/*df*/()
+            """.trimMargin(),
+            LintError(1, 17, ruleId, "${RUN_IN_SCRIPT.warnText()} version = \"0.1.0-SNAPSHOT\"", true),
+            LintError(7, 17, ruleId, "${RUN_IN_SCRIPT.warnText()} foo/*df*/()", true),
+            fileName = "src/main/kotlin/org/cqfn/diktat/Example.kts"
+        )
+    }
 }