Repository: cqfn/diKTat
Pull request #929 (2021-06-16 09:46:39)

Bugfix in the LineLength rule
### What's done:
* Fix bug with NPE in long comments in case when comment located at the line length limit
* Add tests

Explanation:
When we looking for the node, which code len is greater than limit, we should keep in mind, that
if `limit = 120`, it means, that the `119` symbol, i.e. `119` element from code line array exceeds the limit.

But previously, in 
```kotlin
val newNode = node.psi.findElementAt(offset + configuration.lineLength.toInt())!!.node
```
we took the `120` element, which offset is actually `121` and so NPE appeared in 

```kotlin
val indexLastSpace = wrongNode.text.substring(0, configuration.lineLength.toInt() - leftOffset).lastIndexOf(' ')
```

since `configuration.lineLength.toInt() == 120` and `leftOffset == 121`

codecov[bot]: # [Codecov](https://codecov.io/gh/cqfn/diKTat/pull/929?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn) Report
> Merging [#929](https://codecov.io/gh/cqfn/diKTat/pull/929?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn) (ac13c13) into [master](https://codecov.io/gh/cqfn/diKTat/commit/6ee9d433a514939c8906f320c3185c27d36e7d26?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn) (6ee9d43) will **not change** coverage.
> The diff coverage is `100.00%`.

> :exclamation: Current head ac13c13 differs from pull request most recent head a2105e8. Consider uploading reports for the commit a2105e8 to get more accurate results
[![Impacted file tree graph](https://codecov.io/gh/cqfn/diKTat/pull/929/graphs/tree.svg?width=650&height=150&src=pr&token=P2quRDffpt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn)](https://codecov.io/gh/cqfn/diKTat/pull/929?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn)

```diff
@@            Coverage Diff            @@
##             master     #929   +/-   ##
=========================================
  Coverage     84.14%   84.14%           
  Complexity     2373     2373           
=========================================
  Files           101      101           
  Lines          6006     6006           
  Branches       1772     1772           
=========================================
  Hits           5054     5054           
  Misses          263      263           
  Partials        689      689           
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `84.14% <100.00%> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/cqfn/diKTat/pull/929?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn) | Coverage Δ | |
|---|---|---|
| [...g/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt](https://codecov.io/gh/cqfn/diKTat/pull/929/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn#diff-ZGlrdGF0LXJ1bGVzL3NyYy9tYWluL2tvdGxpbi9vcmcvY3Fmbi9kaWt0YXQvcnVsZXNldC9ydWxlcy9jaGFwdGVyMy9MaW5lTGVuZ3RoLmt0) | `91.78% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/cqfn/diKTat/pull/929?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/cqfn/diKTat/pull/929?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn). Last update [6ee9d43...a2105e8](https://codecov.io/gh/cqfn/diKTat/pull/929?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cqfn).


Patch:
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt
index 518f99f7..5ea3e440 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/LineLength.kt
@@ -83,7 +83,7 @@ class LineLength(configRules: List<RulesConfig>) : DiktatRule(
         var offset = 0
         node.text.lines().forEach { line ->
             if (line.length > configuration.lineLength) {
-                val newNode = node.psi.findElementAt(offset + configuration.lineLength.toInt())!!.node
+                val newNode = node.psi.findElementAt(offset + configuration.lineLength.toInt() - 1)!!.node
                 if ((newNode.elementType != KDOC_TEXT && newNode.elementType != KDOC_MARKDOWN_INLINE_LINK) ||
                         !isKdocValid(newNode)) {
                     positionByOffset = node.treeParent.calculateLineColByOffset()


Test patch:
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/LineLengthFixTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/LineLengthFixTest.kt
index 1568cfbf..e0f317ae 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/LineLengthFixTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/LineLengthFixTest.kt
@@ -7,6 +7,10 @@ import org.cqfn.diktat.util.FixTestBase
 import org.junit.jupiter.api.Test
 
 class LineLengthFixTest : FixTestBase("test/paragraph3/long_line", ::LineLength) {
+    private val rulesConfigListDefaultLineLength: List<RulesConfig> = listOf(
+        RulesConfig(LONG_LINE.name, true,
+            mapOf("lineLength" to "120"))
+    )
     private val rulesConfigListLineLength: List<RulesConfig> = listOf(
         RulesConfig(LONG_LINE.name, true,
             mapOf("lineLength" to "50"))
@@ -21,6 +25,11 @@ class LineLengthFixTest : FixTestBase("test/paragraph3/long_line", ::LineLength)
         fixAndCompare("LongLineCommentExpected.kt", "LongLineCommentTest.kt", rulesConfigListLineLength)
     }
 
+    @Test
+    fun `should not fix long comment which located on the line length limit`() {
+        fixAndCompare("LongLineCommentExpected2.kt", "LongLineCommentTest2.kt", rulesConfigListDefaultLineLength)
+    }
+
     @Test
     fun `should fix long binary expression`() {
         fixAndCompare("LongLineExpressionExpected.kt", "LongLineExpressionTest.kt", rulesConfigListLineLength)
diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentExpected.kt
index 444c5ad5..bd48fd85 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentExpected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentExpected.kt
@@ -13,4 +13,4 @@ fun foo() {
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentExpected2.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentExpected2.kt
new file mode 100644
index 00000000..f9b6d642
--- /dev/null
+++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentExpected2.kt
@@ -0,0 +1,10 @@
+/**
+ * This rule checks if there is a backing property for field with property accessors, in case they don't use field keyword
+ */
+class ImplicitBackingPropertyRuleTest(configRules: List<RulesConfig>) {
+    private fun validateAccessors(node: ASTNode, propsWithBackSymbol: List<String>) {
+        val accessors = node.findAllDescendantsWithSpecificType(PROPERTY_ACCESSOR).filter { it.hasChildOfType(BLOCK) }  // Comment, which shouldn't be moved
+        accessors.filter { it.hasChildOfType(GET_KEYWORD) }.forEach { handleGetAccessors(it, node, propsWithBackSymbol) }
+        accessors.filter { it.hasChildOfType(SET_KEYWORD) }.forEach { handleSetAccessors(it, node, propsWithBackSymbol) }
+    }
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentTest.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentTest.kt
index e173ac56..8760b872 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentTest.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentTest.kt
@@ -11,4 +11,4 @@ fun foo() {
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentTest2.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentTest2.kt
new file mode 100644
index 00000000..f9b6d642
--- /dev/null
+++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineCommentTest2.kt
@@ -0,0 +1,10 @@
+/**
+ * This rule checks if there is a backing property for field with property accessors, in case they don't use field keyword
+ */
+class ImplicitBackingPropertyRuleTest(configRules: List<RulesConfig>) {
+    private fun validateAccessors(node: ASTNode, propsWithBackSymbol: List<String>) {
+        val accessors = node.findAllDescendantsWithSpecificType(PROPERTY_ACCESSOR).filter { it.hasChildOfType(BLOCK) }  // Comment, which shouldn't be moved
+        accessors.filter { it.hasChildOfType(GET_KEYWORD) }.forEach { handleGetAccessors(it, node, propsWithBackSymbol) }
+        accessors.filter { it.hasChildOfType(SET_KEYWORD) }.forEach { handleSetAccessors(it, node, propsWithBackSymbol) }
+    }
+}
diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineRValueExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineRValueExpected.kt
index d0f6f464..a2c047bb 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineRValueExpected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineRValueExpected.kt
@@ -27,6 +27,8 @@ fun foo() {
     val longStringExpression = "First very long" +
 " part" + "second Part"
 
+    val longStringExpression2 =                   "String starts at the line len limit"
+
     val veryLooooooooooooooooooooooooooooooongVal = "text"
 
     val longIntExpression = (12345 + 12345 +
diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineRValueTest.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineRValueTest.kt
index 2a8e120e..346e4838 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineRValueTest.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineRValueTest.kt
@@ -21,6 +21,8 @@ fun foo() {
 
     val longStringExpression = "First very long part" + "second Part"
 
+    val longStringExpression2 =                   "String starts at the line len limit"
+
     val veryLooooooooooooooooooooooooooooooongVal = "text"
 
     val longIntExpression = (12345 + 12345 + 12345 + 12345)