From 5e521176d40d8853f14c816ba62361fedb588421 Mon Sep 17 00:00:00 2001 From: Arrgentum Date: Fri, 20 May 2022 18:56:15 +0300 Subject: [PATCH] ### Whats added: * Start logic to fix and warn String Template in Linelength rule --- .../ruleset/rules/chapter3/LineLength.kt | 101 ++++++++++++------ .../cqfn/diktat/ruleset/utils/AstNodeUtils.kt | 4 +- .../ruleset/chapter3/LineLengthFixTest.kt | 16 ++- .../ruleset/chapter3/LineLengthWarnTest.kt | 9 +- .../long_line/LongBinaryExpressionExpected.kt | 4 +- .../LongDotQualifiedExpressionExpected.kt | 4 + .../long_line/LongLineAnnotationExpected.kt | 8 +- .../long_line/LongLineFunExpected.kt | 9 +- .../long_line/LongLineRValueExpected.kt | 11 +- .../long_line/LongShortRValueExpected.kt | 3 +- 10 files changed, 117 insertions(+), 52 deletions(-) create mode 100644 diktat-rules/src/test/resources/test/paragraph3/long_line/LongDotQualifiedExpressionExpected.kt 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 80b8debde2..7b3679db9d 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 @@ -54,16 +54,10 @@ import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE import com.pinterest.ktlint.core.ast.isWhiteSpace import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline -import com.pinterest.ktlint.core.ast.nextSibling -import com.pinterest.ktlint.core.ast.parent -import com.pinterest.ktlint.core.ast.prevSibling import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType -import org.jetbrains.kotlin.psi.parameterRecursiveVisitor -import org.jetbrains.kotlin.psi.psiUtil.parents import java.net.MalformedURLException import java.net.URL @@ -131,18 +125,34 @@ class LineLength(configRules: List) : DiktatRule( ) private fun isFixable(wrongNode: ASTNode, configuration: LineLengthConfiguration): LongLineFixableCases { var parent = wrongNode + var stringOrDot : ASTNode? = null do { when (parent.elementType) { BINARY_EXPRESSION, PARENTHESIZED -> { - val splitOffset = searchRightSplitAfterType(parent, configuration, OPERATION_REFERENCE)?.second - splitOffset?.let { - if (isConditionToUpAnalysisBinExpression(parent, splitOffset)) { - parent = parent.treeParent - } else { - return checkBinaryExpression(parent, configuration) + parent.findParentNodeWithSpecificType(VALUE_ARGUMENT_LIST) ?. let { + parent = it + } ?: parent.findParentNodeWithSpecificType(FUNCTION_LITERAL) ?. let { + parent = it + } ?: run { + val splitOffset = searchRightSplitAfterType(parent, configuration, OPERATION_REFERENCE)?.second + splitOffset?.let { + val parentIsBiExprOrParenthesized = parent.treeParent.elementType in listOf(BINARY_EXPRESSION, PARENTHESIZED) + val parentIsFunOrProperty = parent.treeParent.elementType in listOf(FUN, PROPERTY) + if (parentIsBiExprOrParenthesized) { + parent = parent.treeParent + } else if (parentIsFunOrProperty && splitOffset >= configuration.lineLength) { + if (stringOrDot != null) { + val returnElem = checkStringTemplateAndDotQualifiedExpression(parent, configuration) + if (returnElem !is None) + return returnElem + } + parent = parent.treeParent + } else { + return checkBinaryExpression(parent, configuration) + } } + ?: run { parent = parent.treeParent } } - ?: run { parent = parent.treeParent } } FUN, PROPERTY -> return checkFunAndProperty(parent) CONDITION -> return checkCondition(parent, configuration) @@ -154,6 +164,7 @@ class LineLength(configRules: List) : DiktatRule( EOL_COMMENT -> return checkComment(parent, configuration) FUNCTION_LITERAL -> return Lambda(parent) STRING_TEMPLATE, DOT_QUALIFIED_EXPRESSION -> { + stringOrDot = parent parent.findParentNodeWithSpecificType(BINARY_EXPRESSION) ?. let { parent = it } ?: parent.findParentNodeWithSpecificType(VALUE_ARGUMENT_LIST) ?. let { @@ -178,9 +189,8 @@ class LineLength(configRules: List) : DiktatRule( */ private fun isConditionToUpAnalysisBinExpression(parent: ASTNode, offset: Int): Boolean { val parentIsBiExprOrParenthesized = parent.treeParent.elementType in listOf(BINARY_EXPRESSION, PARENTHESIZED) - val parentIsFunctionLiteral = parent.treeParent.treeParent.elementType == FUNCTION_LITERAL val parentIsFunOrProperty = parent.treeParent.elementType in listOf(FUN, PROPERTY) - return (parentIsBiExprOrParenthesized || parentIsFunctionLiteral || (parentIsFunOrProperty && offset >= configuration.lineLength)) + return (parentIsBiExprOrParenthesized || (parentIsFunOrProperty && offset >= configuration.lineLength)) } /** @@ -196,7 +206,7 @@ class LineLength(configRules: List) : DiktatRule( return LongBinaryExpression(node, configuration, leftOffset, binList) } - private fun checkStringTemplateAndDotQualifiedExpression(node: ASTNode, configuration: LineLengthConfiguration, funOrPropertyNode : ASTNode?) : LongLineFixableCases { + private fun checkStringTemplateAndDotQualifiedExpression(node: ASTNode, configuration: LineLengthConfiguration, funOrPropertyNode : ASTNode? = null) : LongLineFixableCases { funOrPropertyNode ?.let { if (it.hasChildOfType(EQ)) { val positionByOffset = positionByOffset(it.getFirstChildWithType(EQ)!!.startOffset).second @@ -276,7 +286,7 @@ class LineLength(configRules: List) : DiktatRule( private fun parserDotQualifiedExpression(wrongNode: ASTNode, configuration: LineLengthConfiguration) : LongLineFixableCases { val nodeDot = searchRightSplitAfterType(wrongNode, configuration, DOT)?.first nodeDot ?. let { - return DotQualifiedExpression(wrongNode, it) + return DotQualifiedExpression(wrongNode) } ?: return None() } @@ -345,17 +355,18 @@ class LineLength(configRules: List) : DiktatRule( private fun fixDotQualifiedExpression(wrongDotQualifiedExpression: DotQualifiedExpression) { val node = wrongDotQualifiedExpression.node - val dot = wrongDotQualifiedExpression.nodeForSplit + val dot = node.getFirstChildWithType(DOT) node.appendNewlineMergingWhiteSpace(dot,dot) } private fun fixArgumentList(wrongArgumentList: ValueArgumentList) { val node = wrongArgumentList.node - val comma = wrongArgumentList.nodeForSplit + val nodeWithComma = wrongArgumentList.nodeForSplit + val comma = nodeWithComma?.getFirstChildWithType(COMMA) comma ?. let { - node.appendNewlineMergingWhiteSpace(comma, comma) + nodeWithComma.appendNewlineMergingWhiteSpace(comma, comma) } ?: run { node.appendNewlineMergingWhiteSpace(node.findChildByType(LPAR)!!.treeNext, node.findChildByType(LPAR)!!.treeNext) - node.appendNewlineMergingWhiteSpace(node.findChildByType(RPAR)!!.treePrev, node.findChildByType(RPAR)!!.treePrev) + node.appendNewlineMergingWhiteSpace(node.findChildByType(RPAR), node.findChildByType(RPAR)) node.getChildren(null).forEach { elem-> if (elem.elementType == COMMA && elem.treeNext.elementType != RPAR) node.appendNewlineMergingWhiteSpace(elem.treeNext, elem.treeNext) @@ -426,13 +437,13 @@ class LineLength(configRules: List) : DiktatRule( val incorrectText = wrongStringTemplate.node.text val firstPart = incorrectText.substring(0, wrongStringTemplate.delimiterIndex) val secondPart = incorrectText.substring(wrongStringTemplate.delimiterIndex, incorrectText.length) - val textBetwenParts = + val textBetweenParts = if (wrongStringTemplate.isOneLineString) { "\" +\n\"" } else { "\n" } - val correctNode = KotlinParser().createNode("$firstPart$textBetwenParts$secondPart") + val correctNode = KotlinParser().createNode("$firstPart$textBetweenParts$secondPart") wrongStringTemplate.node.treeParent.replaceChild(wrongStringTemplate.node, correctNode) } @@ -485,6 +496,32 @@ class LineLength(configRules: List) : DiktatRule( } } + private fun searchComma(node: ASTNode, commaList: MutableList) { + if (node.elementType == VALUE_ARGUMENT_LIST){ + node.getChildren(null) + .filter { + it.elementType == VALUE_ARGUMENT_LIST + } + .forEach { + searchComma(it, commaList) + } + commaList.add(node) + } + } + + private fun searchDot(node: ASTNode, DotList: MutableList) { + if (node.elementType == DOT_QUALIFIED_EXPRESSION){ + node.getChildren(null) + .filter { + it.elementType == DOT_QUALIFIED_EXPRESSION + } + .forEach { + searchDot(it, DotList) + } + DotList.add(node) + } + } + /** * This method stored all the nodes that have BINARY_EXPRESSION or PREFIX_EXPRESSION element type. * Return List of the Pair @@ -538,15 +575,19 @@ class LineLength(configRules: List) : DiktatRule( */ @Suppress("UnsafeCallOnNullableType") private fun searchRightSplitAfterType(parent: ASTNode, configuration: LineLengthConfiguration, type: IElementType): Pair? { - val binList: MutableList = mutableListOf() - searchBinaryExpression(parent, binList) - return binList.map { - it to positionByOffset(it.getFirstChildWithType(type)!!.startOffset).second + val list: MutableList = mutableListOf() + when (type) { + OPERATION_REFERENCE -> searchBinaryExpression(parent, list) + COMMA -> searchComma(parent, list) + DOT -> searchDot(parent, list) + } + return list.map { + it to positionByOffset(it.getFirstChildWithType(type)?.startOffset ?: configuration.lineLength.toInt()).second } .sortedBy { it.second } .reversed() .firstOrNull { (it, offset) -> - offset + (it.getFirstChildWithType(type)?.text!!.length ?: 0) <= configuration.lineLength + 1 + offset + (it.getFirstChildWithType(type)?.text?.length ?: 0) <= configuration.lineLength + 1 } } @@ -637,9 +678,9 @@ class LineLength(configRules: List) : DiktatRule( private class Lambda(node: ASTNode) : LongLineFixableCases(node) /** - * Class DotQualifiedExpression show that line should be split in DotQualifiedExpression after node [nodeForSplit] + * Class DotQualifiedExpression show that line should be split in DotQualifiedExpression */ - private class DotQualifiedExpression(node: ASTNode, val nodeForSplit: ASTNode) : LongLineFixableCases(node) + private class DotQualifiedExpression(node: ASTNode) : LongLineFixableCases(node) /** * Class ValueArgumentList show that line should be split in ValueArgumentList: 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 8a5ef64524..e1205cd49b 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 @@ -552,7 +552,9 @@ fun ASTNode.leaveExactlyNumNewLines(num: Int) { */ fun ASTNode.appendNewlineMergingWhiteSpace(whiteSpaceNode: ASTNode?, beforeNode: ASTNode?) { if (whiteSpaceNode != null && whiteSpaceNode.elementType == WHITE_SPACE) { - (whiteSpaceNode as LeafPsiElement).rawReplaceWithText("\n${whiteSpaceNode.text}") + if (whiteSpaceNode.text.lines().size == 1) { + (whiteSpaceNode as LeafPsiElement).rawReplaceWithText("\n${whiteSpaceNode.text}") + } } else { addChild(PsiWhiteSpaceImpl("\n"), beforeNode) } 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 8dfc008cf4..e1a79d7a85 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 @@ -70,8 +70,13 @@ class LineLengthFixTest : FixTestBase("test/paragraph3/long_line", ::LineLength) } @Test - fun `shouldn't fix`() { - fixAndCompare("LongExpressionNoFixExpected.kt", "LongExpressionNoFixTest.kt", rulesConfigListShortLineLength) + fun `shouldn't fix1`() { + fixAndCompare("LongExpressionNoFixExpected1.kt", "LongExpressionNoFixTest.kt", rulesConfigListShortLineLength) + } + + @Test + fun `shouldn't fix2`() { + fixAndCompare("LongExpressionNoFixExpected2.kt", "LongExpressionNoFixTest.kt", rulesConfigListShortLineLength) } @Test @@ -88,4 +93,11 @@ class LineLengthFixTest : FixTestBase("test/paragraph3/long_line", ::LineLength) fun `fix expression in condition`() { fixAndCompare("LongExpressionInConditionExpected.kt", "LongExpressionInConditionTest.kt", rulesConfigListLineLength) } + + @Test + fun `fix long Dot Qualified Expression`() { + fixAndCompare("LongDotQualifiedExpressionExpected.kt", "LongDotQualifiedExpressionTest.kt", rulesConfigListLineLength) + } + + } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/LineLengthWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/LineLengthWarnTest.kt index 7eec5ab34b..28311e8b87 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/LineLengthWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/LineLengthWarnTest.kt @@ -141,8 +141,8 @@ class LineLengthWarnTest : LintTestBase(::LineLength) { | } |} """.trimMargin(), - LintError(14, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 143", false), - LintError(18, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 142", false) + LintError(14, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 143", true), + LintError(18, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 142", true) ) } @@ -223,7 +223,8 @@ class LineLengthWarnTest : LintTestBase(::LineLength) { |} """.trimMargin(), LintError(8, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 130", false), - LintError(9, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 123", false) + LintError(9, 1, ruleId, "${LONG_LINE.warnText()} max line length 120, but was 123", true) + ) } @@ -235,7 +236,7 @@ class LineLengthWarnTest : LintTestBase(::LineLength) { |@Query(value = "ASDAASDASDASDASDASDASDASDAASDASDASDASDASDASDASDAASDASDASDASDASDASD") |fun foo() = println("ASDAASDASDASDASDASDASDASDAASDASDASDASDASDASDASDAASDASDASDASDASDASD") """.trimMargin(), - LintError(1, 1, ruleId, "${LONG_LINE.warnText()} max line length 40, but was 84", false), + LintError(1, 1, ruleId, "${LONG_LINE.warnText()} max line length 40, but was 84", true), LintError(2, 1, ruleId, "${LONG_LINE.warnText()} max line length 40, but was 89", true), rulesConfigList = shortLineLength ) diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongBinaryExpressionExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongBinaryExpressionExpected.kt index 432b811517..abc66c3d7c 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongBinaryExpressionExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongBinaryExpressionExpected.kt @@ -39,8 +39,8 @@ fun foo() { val variable = Methoooooooooooooooooooooooooood() ?: "some loooooong string" - val variable = Methooooood() ?: "some" + -" looong string" + val variable = Methooooood() + ?: "some looong string" var headerKdoc = firstCodeNode.prevSibling { it.elementType == KDOC diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongDotQualifiedExpressionExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongDotQualifiedExpressionExpected.kt new file mode 100644 index 0000000000..b25f02b974 --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongDotQualifiedExpressionExpected.kt @@ -0,0 +1,4 @@ +package test.paragraph3.long_line + +val A = This.Is.Veeeeryyyyyyy.Loooooong.Dot +.Qualified.Expression \ No newline at end of file diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineAnnotationExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineAnnotationExpected.kt index e32daa3b6c..ca87b3f0d4 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineAnnotationExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineAnnotationExpected.kt @@ -1,11 +1,11 @@ package test.paragraph3.long_line -@Query(value = "select * from test inner join" + -" test_execution on test.id = test_execution.test_id and test_execution.st", nativeQuery = true) +@Query( +value = "select * from test inner join test_execution on test.id = test_execution.test_id and test_execution.st", + nativeQuery = true) fun retrieveBatches(limit: Int, offset: Int, executionId: Long): Some -@Query(value = "select * from test inner join" + -" test_execution on test.id = test_execution.test_id and test_execution.status = 'READY' and test_execution.test_suite_execution_id = ?3 limit ?1 offset ?2", nativeQuery = true) +@Query(value = "select * from test inner join test_execution on test.id = test_execution.test_id and test_execution.status = 'READY' and test_execution.test_suite_execution_id = ?3 limit ?1 offset ?2", nativeQuery = true) fun some(limit: Int, offset: Int, executionId: Long): List @Query(value = "select * from test inner joi", nativeQuery = true) diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineFunExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineFunExpected.kt index 476a99c1d6..937c5ea153 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineFunExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongLineFunExpected.kt @@ -1,6 +1,9 @@ package test.paragraph3.long_line -fun foo() = - println("fhdbsfkhfbvsjfkvbhjdksfvhbhhjhjhjnaljfbkshvjdsjdnlvbdkhkjncdkljskbfvhdsjndlvfkbdhfjdncjsdcscsdcsdcsdcdd") +fun foo() = println( +"fhdbsfkhfbvsjfkvbhjdksfvhbhhjhjhjnaljfbkshvjdsjdnlvbdkhkjncdkljskbfvhdsjndlvfkbdhfjdncjsdcscsdcsdcsdcdd" +) -fun foo () { println("fhdbsfkhfbvsjfkvbhjdksfvhbhhjhjhjnaljfbkshvjdsjdnlvbdkhkjncdkljskbfvhdsjndlvfkbdhfjdncjsdcscsdcsdcsdcdd") } \ No newline at end of file +fun foo () { println( +"fhdbsfkhfbvsjfkvbhjdksfvhbhhjhjhjnaljfbkshvjdsjdnlvbdkhkjncdkljskbfvhdsjndlvfkbdhfjdncjsdcscsdcsdcsdcdd" +) } \ No newline at end of file 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 06fc6dc392..4e48223507 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 @@ -7,14 +7,14 @@ fun foo() { fun foo() { val veryLoooooooooooooooooongNamesList = listOf("Jack", "Nick") - veryLoooooooooooooooooongNamesList.forEach { + veryLoooooooooooooooooongNamesList +.forEach { name -> if (name == "Nick") { veryLoooooooooooooooooongNamesList.map { val str = "This string shouldn't be split"} name.map { val str = "This string should be split" } } - } } @@ -33,10 +33,11 @@ fun foo() { val longStringExpression = "First part" + "second Part" - val longStringExpression = "First" + "second Part" + val longStringExpression = "First" + + "second Part" - val longStringExpression = "First very long" + -" part" + "second Part" + val longStringExpression = + "First very long part" + "second Part" val longStringExpression2 = "String starts at the line len limit" diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongShortRValueExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongShortRValueExpected.kt index 5b9c951977..413e655a03 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongShortRValueExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongShortRValueExpected.kt @@ -1,3 +1,4 @@ package test.paragraph3.long_line -val LongWithVar2 = "${s + "a"} is a string" \ No newline at end of file +val LongWithVar2 = + "${s + "a"} is a string" \ No newline at end of file