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 36417b17d2..546c99a53f 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 @@ -5,14 +5,25 @@ import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.getRuleConfig import org.cqfn.diktat.ruleset.constants.Warnings.LONG_LINE import org.cqfn.diktat.ruleset.rules.DiktatRule -import org.cqfn.diktat.ruleset.utils.* +import org.cqfn.diktat.ruleset.utils.KotlinParser +import org.cqfn.diktat.ruleset.utils.appendNewlineMergingWhiteSpace +import org.cqfn.diktat.ruleset.utils.calculateLineColByOffset +import org.cqfn.diktat.ruleset.utils.findAllNodesWithConditionOnLine +import org.cqfn.diktat.ruleset.utils.findParentNodeWithSpecificType +import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType +import org.cqfn.diktat.ruleset.utils.getFirstChildWithType +import org.cqfn.diktat.ruleset.utils.getLineNumber +import org.cqfn.diktat.ruleset.utils.hasChildOfType import com.pinterest.ktlint.core.ast.ElementType.ANDAND -import com.pinterest.ktlint.core.ast.ElementType.ANNOTATION_ENTRY +import com.pinterest.ktlint.core.ast.ElementType.ARROW import com.pinterest.ktlint.core.ast.ElementType.BINARY_EXPRESSION +import com.pinterest.ktlint.core.ast.ElementType.BLOCK import com.pinterest.ktlint.core.ast.ElementType.BOOLEAN_CONSTANT import com.pinterest.ktlint.core.ast.ElementType.CHARACTER_CONSTANT -import com.pinterest.ktlint.core.ast.ElementType.CONDITION +import com.pinterest.ktlint.core.ast.ElementType.COMMA +import com.pinterest.ktlint.core.ast.ElementType.DOT +import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.ELVIS import com.pinterest.ktlint.core.ast.ElementType.EOL_COMMENT import com.pinterest.ktlint.core.ast.ElementType.EQ @@ -25,7 +36,6 @@ import com.pinterest.ktlint.core.ast.ElementType.FUN import com.pinterest.ktlint.core.ast.ElementType.FUNCTION_LITERAL import com.pinterest.ktlint.core.ast.ElementType.GT import com.pinterest.ktlint.core.ast.ElementType.GTEQ -import com.pinterest.ktlint.core.ast.ElementType.IF import com.pinterest.ktlint.core.ast.ElementType.IMPORT_LIST import com.pinterest.ktlint.core.ast.ElementType.INTEGER_CONSTANT import com.pinterest.ktlint.core.ast.ElementType.KDOC_MARKDOWN_INLINE_LINK @@ -33,6 +43,7 @@ import com.pinterest.ktlint.core.ast.ElementType.KDOC_TEXT import com.pinterest.ktlint.core.ast.ElementType.LBRACE import com.pinterest.ktlint.core.ast.ElementType.LITERAL_STRING_TEMPLATE_ENTRY import com.pinterest.ktlint.core.ast.ElementType.LONG_STRING_TEMPLATE_ENTRY +import com.pinterest.ktlint.core.ast.ElementType.LPAR import com.pinterest.ktlint.core.ast.ElementType.LT import com.pinterest.ktlint.core.ast.ElementType.LTEQ import com.pinterest.ktlint.core.ast.ElementType.NULL @@ -45,17 +56,21 @@ import com.pinterest.ktlint.core.ast.ElementType.PREFIX_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.PROPERTY import com.pinterest.ktlint.core.ast.ElementType.RBRACE import com.pinterest.ktlint.core.ast.ElementType.REFERENCE_EXPRESSION +import com.pinterest.ktlint.core.ast.ElementType.RPAR +import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS +import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.SHORT_STRING_TEMPLATE_ENTRY import com.pinterest.ktlint.core.ast.ElementType.STRING_TEMPLATE +import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST +import com.pinterest.ktlint.core.ast.ElementType.WHEN_CONDITION_WITH_EXPRESSION +import com.pinterest.ktlint.core.ast.ElementType.WHEN_ENTRY import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE import com.pinterest.ktlint.core.ast.isWhiteSpace import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline 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.psiUtil.parents import java.net.MalformedURLException import java.net.URL @@ -87,24 +102,24 @@ class LineLength(configRules: List) : DiktatRule( } } } + @Suppress("UnsafeCallOnNullableType", "TOO_LONG_FUNCTION") private fun checkLength(node: ASTNode, configuration: LineLengthConfiguration) { var offset = 0 node.text.lines().forEach { line -> if (line.length > configuration.lineLength) { val newNode = node.psi.findElementAt(offset + configuration.lineLength.toInt() - 1)!!.node - if ((newNode.elementType != KDOC_TEXT && newNode.elementType != KDOC_MARKDOWN_INLINE_LINK) || - !isKdocValid(newNode) - ) { + if ((newNode.elementType != KDOC_TEXT && newNode.elementType != KDOC_MARKDOWN_INLINE_LINK) || !isKdocValid(newNode)) { positionByOffset = node.treeParent.calculateLineColByOffset() val fixableType = isFixable(newNode, configuration) - LONG_LINE.warnAndFix(configRules, emitWarn, isFixMode, + LONG_LINE.warnAndFix( + configRules, emitWarn, isFixMode, "max line length ${configuration.lineLength}, but was ${line.length}", offset + node.startOffset, node, fixableType !is None ) { // we should keep in mind, that in the course of fixing we change the offset val textLenBeforeFix = node.textLength - fixError(fixableType) + fixableType.fix() val textLenAfterFix = node.textLength // offset for all next nodes changed to this delta offset += (textLenAfterFix - textLenBeforeFix) @@ -117,41 +132,61 @@ class LineLength(configRules: List) : DiktatRule( @Suppress( "TOO_LONG_FUNCTION", + "LongMethod", "ComplexMethod", + "GENERIC_VARIABLE_WRONG_DECLARATION", ) private fun isFixable(wrongNode: ASTNode, configuration: LineLengthConfiguration): LongLineFixableCases { var parent = wrongNode + var stringOrDot: ASTNode? = null do { when (parent.elementType) { BINARY_EXPRESSION, PARENTHESIZED -> { - val splitOffset = searchRightSplitInBinaryExpression(parent, configuration)?.second - splitOffset?.let { - if (isConditionToUpAnalysisBinExpression(parent, splitOffset)) { - parent = parent.treeParent - } else { - return checkBinaryExpression(parent, configuration) + val parentIsValArgListOrFunLitOrWhenEntry = listOf(VALUE_ARGUMENT_LIST, FUNCTION_LITERAL, WHEN_CONDITION_WITH_EXPRESSION) + findParentNodeMatching(parent, parentIsValArgListOrFunLitOrWhenEntry)?.let { + parent = it + } ?: run { + val splitOffset = searchRightSplitAfterOperationReference(parent, configuration)?.second + splitOffset?.let { + val parentIsBiExprOrParenthesized = parent.treeParent.elementType in listOf(BINARY_EXPRESSION, PARENTHESIZED) + val parentIsFunOrProperty = parent.treeParent.elementType in listOf(FUN, PROPERTY) + if (parentIsBiExprOrParenthesized || (parentIsFunOrProperty && splitOffset >= configuration.lineLength)) { + parent = parent.treeParent + } else { + return checkBinaryExpression(parent, configuration) + } } + ?: run { + stringOrDot?.let { + val returnElem = checkStringTemplateAndDotQualifiedExpression(it, configuration) + if (returnElem !is None) { + return returnElem + } + } + parent = parent.treeParent + } } - ?: run { parent = parent.treeParent } } FUN, PROPERTY -> return checkFunAndProperty(parent) - CONDITION -> return checkCondition(parent, configuration) + VALUE_ARGUMENT_LIST -> parent.findParentNodeWithSpecificType(BINARY_EXPRESSION)?.let { + parent = it + } ?: return checkArgumentsList(parent, configuration) + WHEN_ENTRY -> return WhenEntry(parent) + WHEN_CONDITION_WITH_EXPRESSION -> return None() EOL_COMMENT -> return checkComment(parent, configuration) FUNCTION_LITERAL -> return Lambda(parent) - STRING_TEMPLATE -> { - // as we are going from bottom to top we are excluding - // 1. IF, because it seems that string template is in condition - // 2. FUN with EQ, it seems that new line should be inserted after `=` - parent.findParentNodeWithSpecificType(IF)?.let { - parent = parent.treeParent - } ?: parent.findParentNodeWithSpecificType(FUN)?.let { node -> - // checking that string template is not in annotation - if (node.hasChildOfType(EQ) && !wrongNode.parents().any { it.elementType == ANNOTATION_ENTRY }) { - parent = node - } else { - return checkStringTemplate(parent, configuration) + STRING_TEMPLATE, DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION -> { + stringOrDot = parent + val parentIsBinExpOrValArgListOrWhenEntry = listOf(BINARY_EXPRESSION, VALUE_ARGUMENT_LIST, WHEN_CONDITION_WITH_EXPRESSION) + findParentNodeMatching(parent, parentIsBinExpOrValArgListOrWhenEntry)?.let { + parent = it + } ?: run { + val returnElem = checkStringTemplateAndDotQualifiedExpression(parent, configuration) + if (returnElem !is None) { + return returnElem } - } ?: return checkStringTemplate(parent, configuration) + parent = parent.treeParent + } } else -> parent = parent.treeParent } @@ -159,14 +194,22 @@ class LineLength(configRules: List) : DiktatRule( return None() } - /** - * Analyzes the Binary expression and decides to go higher level with the analysis or analyze at this level - */ - 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)) + private fun findParentNodeMatching(node: ASTNode, listType: List): ASTNode? { + listType.forEach { type -> + node.findParentNodeWithSpecificType(type)?.let { + return it + } + } + return null + } + + private fun checkArgumentsList(node: ASTNode, configuration: LineLengthConfiguration): LongLineFixableCases { + node.findParentNodeWithSpecificType(WHEN_ENTRY)?.let { + it.findChildByType(BLOCK)?.run { + return ValueArgumentList(node, configuration, positionByOffset) + } ?: return WhenEntry(it) + } + return ValueArgumentList(node, configuration, positionByOffset) } /** @@ -179,9 +222,38 @@ class LineLength(configRules: List) : DiktatRule( if (binList.size == 1) { return BinaryExpression(node) } - return LongBinaryExpression(node, configuration, leftOffset, binList) + return LongBinaryExpression(node, configuration, leftOffset, binList, positionByOffset) + } + + @Suppress("TOO_MANY_LINES_IN_LAMBDA", "GENERIC_VARIABLE_WRONG_DECLARATION") + private fun checkStringTemplateAndDotQualifiedExpression( + node: ASTNode, + configuration: LineLengthConfiguration + ): LongLineFixableCases { + val isPropertyOrFun = listOf(PROPERTY, FUN) + val funOrPropertyNode = findParentNodeMatching(node, isPropertyOrFun) + funOrPropertyNode?.let { + if (it.hasChildOfType(EQ)) { + val positionByOffset = positionByOffset(it.getFirstChildWithType(EQ)?.startOffset ?: 0).second + if (positionByOffset < configuration.lineLength / 2) { + val returnedClass = parserStringAndDot(node, configuration) + if (returnedClass !is None) { + return returnedClass + } + } + return FunAndProperty(it) + } + return parserStringAndDot(node, configuration) + } ?: return parserStringAndDot(node, configuration) } + private fun parserStringAndDot(node: ASTNode, configuration: LineLengthConfiguration) = + if (node.elementType == STRING_TEMPLATE) { + parserStringTemplate(node, configuration) + } else { + parserDotQualifiedExpression(node, configuration) + } + /** * This class finds where the string can be split * @@ -190,7 +262,7 @@ class LineLength(configRules: List) : DiktatRule( * None - if the string can't be split */ @Suppress("TOO_LONG_FUNCTION", "UnsafeCallOnNullableType") - private fun checkStringTemplate(node: ASTNode, configuration: LineLengthConfiguration): LongLineFixableCases { + private fun parserStringTemplate(node: ASTNode, configuration: LineLengthConfiguration): LongLineFixableCases { var multiLineOffset = 0 val leftOffset = if (node.text.lines().size > 1) { node @@ -207,7 +279,8 @@ class LineLength(configRules: List) : DiktatRule( } else { positionByOffset(node.startOffset).second } - val delimiterIndex = node.text.substring(0, multiLineOffset + configuration.lineLength.toInt() - leftOffset).lastIndexOf(' ') + val delimiterIndex = + node.text.substring(0, multiLineOffset + configuration.lineLength.toInt() - leftOffset).lastIndexOf(' ') if (delimiterIndex == -1) { // we can't split this string, however may be we can move it entirely: // case when new line should be inserted after `+`. Example: "first" + "second" @@ -227,7 +300,8 @@ class LineLength(configRules: List) : DiktatRule( return None() } // minus 2 here as we are inserting ` +` and we don't want it to exceed line length - val shouldAddTwoSpaces = (multiLineOffset == 0) && (leftOffset + delimiterIndex > configuration.lineLength.toInt() - 2) + val shouldAddTwoSpaces = + (multiLineOffset == 0) && (leftOffset + delimiterIndex > configuration.lineLength.toInt() - 2) val correcterDelimiter = if (shouldAddTwoSpaces) { node.text.substring(0, delimiterIndex - 2).lastIndexOf(' ') } else { @@ -239,6 +313,19 @@ class LineLength(configRules: List) : DiktatRule( return StringTemplate(node, correcterDelimiter, multiLineOffset == 0) } + private fun parserDotQualifiedExpression( + wrongNode: ASTNode, + configuration: LineLengthConfiguration + ): LongLineFixableCases { + val nodeDot = searchRightSplitBeforeDotOrSafeAccess(wrongNode, configuration, DOT) + val nodeSafeAccess = searchRightSplitBeforeDotOrSafeAccess(wrongNode, configuration, SAFE_ACCESS) + return nodeDot?.let { + DotQualifiedExpression(wrongNode) + } ?: nodeSafeAccess?.let { + DotQualifiedExpression(wrongNode) + } ?: None() + } + private fun checkFunAndProperty(wrongNode: ASTNode) = if (wrongNode.hasChildOfType(EQ)) FunAndProperty(wrongNode) else None() @@ -256,16 +343,6 @@ class LineLength(configRules: List) : DiktatRule( return Comment(wrongNode, isNewLine, indexLastSpace + stringBeforeCommentContent.length) } - private fun checkCondition(wrongNode: ASTNode, configuration: LineLengthConfiguration): LongLineFixableCases { - val leftOffset = positionByOffset(wrongNode.firstChildNode.startOffset).second - val binList: MutableList = mutableListOf() - searchBinaryExpression(wrongNode, binList) - if (binList.size == 1) { - return BinaryExpression(wrongNode) - } - return LongBinaryExpression(wrongNode, configuration, leftOffset, binList) - } - // fixme json method private fun isKdocValid(node: ASTNode) = try { if (node.elementType == KDOC_TEXT) { @@ -278,116 +355,6 @@ class LineLength(configRules: List) : DiktatRule( false } - @Suppress("UnsafeCallOnNullableType", "WHEN_WITHOUT_ELSE") - private fun fixError(fixableType: LongLineFixableCases) { - when (fixableType) { - is FunAndProperty -> fixableType.node.appendNewlineMergingWhiteSpace(null, fixableType.node.findChildByType(EQ)!!.treeNext) - is Comment -> fixComment(fixableType) - is LongBinaryExpression -> fixLongBinaryExpression(fixableType) - is BinaryExpression -> fixBinaryExpression(fixableType.node) - is StringTemplate -> fixStringTemplate(fixableType) - is Lambda -> fixLambda(fixableType.node) - is None -> return - } - } - - private fun fixComment(wrongComment: Comment) { - val wrongNode = wrongComment.node - if (wrongComment.hasNewLineBefore) { - val indexLastSpace = wrongComment.indexLastSpace - val nodeText = "//${wrongNode.text.substring(indexLastSpace, wrongNode.text.length)}" - wrongNode.treeParent.apply { - addChild(LeafPsiElement(EOL_COMMENT, wrongNode.text.substring(0, indexLastSpace)), wrongNode) - addChild(PsiWhiteSpaceImpl("\n"), wrongNode) - addChild(LeafPsiElement(EOL_COMMENT, nodeText), wrongNode) - removeChild(wrongNode) - } - } else { - if (wrongNode.treePrev.isWhiteSpace()) { - wrongNode.treeParent.removeChild(wrongNode.treePrev) - } - - val newLineNodeOnPreviousLine = wrongNode.findAllNodesWithConditionOnLine(wrongNode.getLineNumber() - 1) { - it.elementType == WHITE_SPACE && it.textContains('\n') - }?.lastOrNull() - - newLineNodeOnPreviousLine?.let { - val parent = wrongNode.treeParent - parent.removeChild(wrongNode) - newLineNodeOnPreviousLine.treeParent.addChild(wrongNode, newLineNodeOnPreviousLine.treeNext) - newLineNodeOnPreviousLine.treeParent.addChild(PsiWhiteSpaceImpl("\n"), newLineNodeOnPreviousLine.treeNext.treeNext) - } - } - } - - /** - * Fix a binary expression - - * If the transfer is done on the Elvis operator, then transfers it to a new line - * If not on the Elvis operator, then transfers it to a new line after the operation reference - */ - @Suppress("UnsafeCallOnNullableType") - private fun fixBinaryExpression(node: ASTNode) { - val nodeOperationReference = node.findChildByType(OPERATION_REFERENCE) - val nextNode = if (nodeOperationReference!!.firstChildNode.elementType != ELVIS) { - nodeOperationReference.treeNext - } else { - if (nodeOperationReference.treePrev.elementType == WHITE_SPACE) { - nodeOperationReference.treePrev - } else { - nodeOperationReference - } - } - node.appendNewlineMergingWhiteSpace(nextNode, nextNode) - } - - /** - * Splits Lambda expressions - add splits lines, thereby making the lambda expression a separate line - */ - @Suppress("UnsafeCallOnNullableType") - private fun fixLambda(node: ASTNode) { - node.appendNewlineMergingWhiteSpace(node.findChildByType(LBRACE)!!.treeNext, node.findChildByType(LBRACE)!!.treeNext) - node.appendNewlineMergingWhiteSpace(node.findChildByType(RBRACE)!!.treePrev, node.findChildByType(RBRACE)!!.treePrev) - } - - @Suppress("UnsafeCallOnNullableType", "COMMENT_WHITE_SPACE") - private fun fixStringTemplate(wrongStringTemplate: StringTemplate) { - val incorrectText = wrongStringTemplate.node.text - val firstPart = incorrectText.substring(0, wrongStringTemplate.delimiterIndex) - val secondPart = incorrectText.substring(wrongStringTemplate.delimiterIndex, incorrectText.length) - val textBetwenParts = - if (wrongStringTemplate.isOneLineString) { - "\" +\n\"" - } else { - "\n" - } - val correctNode = KotlinParser().createNode("$firstPart$textBetwenParts$secondPart") - wrongStringTemplate.node.treeParent.replaceChild(wrongStringTemplate.node, correctNode) - } - - /** - * Finds where it is better to fix a Binary expression and fixes it - */ - @Suppress("UnsafeCallOnNullableType") - private fun fixLongBinaryExpression(wrongBinaryExpression: LongBinaryExpression) { - val anySplitNode = searchSomeSplitInBinaryExpression(wrongBinaryExpression.node, wrongBinaryExpression.maximumLineLength) - val rigthSplitnode = anySplitNode[0] ?: anySplitNode[1] ?: anySplitNode[2] - val nodeOperationReference = rigthSplitnode?.first?.getFirstChildWithType(OPERATION_REFERENCE) - rigthSplitnode?.let { - val nextNode = if (nodeOperationReference!!.firstChildNode.elementType != ELVIS) { - nodeOperationReference.treeNext - } else { - if (nodeOperationReference.treePrev.elementType == WHITE_SPACE) { - nodeOperationReference.treePrev - } else { - nodeOperationReference - } - } - if (!nextNode.text.contains(("\n"))) { - rigthSplitnode.first.appendNewlineMergingWhiteSpace(nextNode, nextNode) - } - } - } - /** * This method uses recursion to store binary node in the order in which they are located * Also binList contains nodes with PREFIX_EXPRESSION element type ( !isFoo(), !isValid) @@ -396,13 +363,8 @@ class LineLength(configRules: List) : DiktatRule( *@param binList mutable list of ASTNode to store nodes */ private fun searchBinaryExpression(node: ASTNode, binList: MutableList) { - if (node.hasChildOfType(BINARY_EXPRESSION) || node.hasChildOfType(PARENTHESIZED) || - node.hasChildOfType(POSTFIX_EXPRESSION)) { + if (node.hasChildOfType(BINARY_EXPRESSION) || node.hasChildOfType(PARENTHESIZED) || node.hasChildOfType(POSTFIX_EXPRESSION)) { node.getChildren(null) - .filter { - it.elementType == BINARY_EXPRESSION || it.elementType == PARENTHESIZED || - it.elementType == POSTFIX_EXPRESSION - } .forEach { searchBinaryExpression(it, binList) } @@ -414,67 +376,68 @@ class LineLength(configRules: List) : DiktatRule( } /** - * This method stored all the nodes that have BINARY_EXPRESSION or PREFIX_EXPRESSION element type. - * Return List of the Pair - * First elem in List - Logic Binary Expression (&& ||) - * Second elem in List - Comparison Binary Expression (> < == >= <= !=) - * Other types (Arithmetical and Bit operation) (+ - * / % >> << *= += -= /= %= ++ -- ! in !in etc) + * This method uses recursion to store dot qualified expression node in the order in which they are located + * Also dotList contains nodes with PREFIX_EXPRESSION element type ( !isFoo(), !isValid)) + * + *@param node node in which to search + *@param dotList mutable list of ASTNode to store nodes */ - @Suppress("TYPE_ALIAS", "UnsafeCallOnNullableType") - private fun searchSomeSplitInBinaryExpression(parent: ASTNode, configuration: LineLengthConfiguration): List?> { - val logicListOperationReference = listOf(OROR, ANDAND) - val compressionListOperationReference = listOf(GT, LT, EQEQ, GTEQ, LTEQ, EXCLEQ) - val binList: MutableList = mutableListOf() - searchBinaryExpression(parent, binList) - val rightBinList = binList.map { - it to positionByOffset(it.getFirstChildWithType(OPERATION_REFERENCE)!!.startOffset).second - } - .sortedBy { it.second } - .reversed() - val returnList: MutableList?> = mutableListOf() - addInSmartListBinExpression(returnList, rightBinList, logicListOperationReference, configuration) - addInSmartListBinExpression(returnList, rightBinList, compressionListOperationReference, configuration) - val expression = rightBinList.firstOrNull { (it, offset) -> - val binOperationReference = it.getFirstChildWithType(OPERATION_REFERENCE)!!.firstChildNode.elementType - offset + (it.getFirstChildWithType(OPERATION_REFERENCE)?.text!!.length ?: 0) <= configuration.lineLength + 1 && - binOperationReference !in logicListOperationReference && binOperationReference !in compressionListOperationReference && binOperationReference != EXCL + private fun searchDotOrSafeAccess(node: ASTNode, dotList: MutableList) { + if (node.elementType == DOT_QUALIFIED_EXPRESSION || node.elementType == SAFE_ACCESS_EXPRESSION || node.elementType == POSTFIX_EXPRESSION) { + node.getChildren(null) + .forEach { + searchDotOrSafeAccess(it, dotList) + } + if (node.elementType != POSTFIX_EXPRESSION) { + dotList.add(node) + } } - returnList.add(expression) - return returnList } /** - * Runs through the sorted list [rightBinList], finds its last element, the type of which is included in the set [typesList] and adds it in the list [returnList] + * Finds the first binary expression closer to the separator */ - @Suppress("TYPE_ALIAS", "UnsafeCallOnNullableType") - private fun addInSmartListBinExpression( - returnList: MutableList?>, - rightBinList: List>, - typesList: List, - configuration: LineLengthConfiguration - ) { - val expression = rightBinList.firstOrNull { (it, offset) -> - val binOperationReference = it.getFirstChildWithType(OPERATION_REFERENCE) - offset + (it.getFirstChildWithType(OPERATION_REFERENCE)?.text!!.length ?: 0) <= configuration.lineLength + 1 && - binOperationReference!!.firstChildNode.elementType in typesList - } - returnList.add(expression) + @Suppress("UnsafeCallOnNullableType") + private fun searchRightSplitAfterOperationReference( + parent: ASTNode, + configuration: LineLengthConfiguration, + ): Pair? { + val list: MutableList = mutableListOf() + searchBinaryExpression(parent, list) + return list.asSequence() + .map { + it to positionByOffset(it.getFirstChildWithType(OPERATION_REFERENCE)!!.startOffset).second + } + .sortedBy { it.second } + .lastOrNull { (it, offset) -> + offset + (it.getFirstChildWithType(OPERATION_REFERENCE)?.text?.length ?: 0) <= configuration.lineLength + 1 + } } /** - * Finds the first binary expression closer to the separator + * Finds the first dot or safe access closer to the separator */ - @Suppress("UnsafeCallOnNullableType") - private fun searchRightSplitInBinaryExpression(parent: ASTNode, configuration: LineLengthConfiguration): Pair? { - val binList: MutableList = mutableListOf() - searchBinaryExpression(parent, binList) - return binList.map { - it to positionByOffset(it.getFirstChildWithType(OPERATION_REFERENCE)!!.startOffset).second - } + @Suppress("MAGIC_NUMBER", "MagicNumber") + private fun searchRightSplitBeforeDotOrSafeAccess( + parent: ASTNode, + configuration: LineLengthConfiguration, + type: IElementType + ): Pair? { + val list: MutableList = mutableListOf() + searchDotOrSafeAccess(parent, list) + val offsetFromMaximum = 10 + return list.asSequence() + .map { + val offset = it.getFirstChildWithType(type)?.run { + positionByOffset(this.startOffset).second + } ?: run { + configuration.lineLength.toInt() + offsetFromMaximum + } + it to offset + } .sortedBy { it.second } - .reversed() - .firstOrNull { (it, offset) -> - offset + (it.getFirstChildWithType(OPERATION_REFERENCE)?.text!!.length ?: 0) <= configuration.lineLength + 1 + .lastOrNull { (_, offset) -> + offset <= configuration.lineLength + 1 } } @@ -493,36 +456,58 @@ class LineLength(configRules: List) : DiktatRule( * Class LongLineFixableCases is parent class for several specific error classes */ @Suppress("KDOC_NO_CONSTRUCTOR_PROPERTY", "MISSING_KDOC_CLASS_ELEMENTS") // todo add proper docs - sealed class LongLineFixableCases(val node: ASTNode) + abstract class LongLineFixableCases(val node: ASTNode) { + /** + * Abstract fix - fix anything nodes + */ + abstract fun fix() + } /** * Class None show error long line have unidentified type or something else that we can't analyze */ - private class None : LongLineFixableCases(KotlinParser().createNode("ERROR")) + private class None : LongLineFixableCases(KotlinParser().createNode("ERROR")) { + @Suppress("EmptyFunctionBlock") + override fun fix() {} + } /** - * @property node node + * Class Comment show that long line should be split in comment * @property hasNewLineBefore flag to handle type of comment: ordinary comment (long part of which should be moved to the next line) * and inline comments (which should be moved entirely to the previous line) * @property indexLastSpace index of last space to substring comment */ - - /** - * Class Comment show that long line should be split in comment - * @property hasNewLineBefore - * @property indexLastSpace - */ private class Comment( node: ASTNode, val hasNewLineBefore: Boolean, val indexLastSpace: Int = 0 - ) : LongLineFixableCases(node) - - /** - * @property node node - * @property delimiterIndex index to split - * @property isOneLineString flag is string is one line - */ + ) : LongLineFixableCases(node) { + override fun fix() { + if (this.hasNewLineBefore) { + val indexLastSpace = this.indexLastSpace + val nodeText = "//${node.text.substring(indexLastSpace, node.text.length)}" + node.treeParent.apply { + addChild(LeafPsiElement(EOL_COMMENT, node.text.substring(0, indexLastSpace)), node) + addChild(PsiWhiteSpaceImpl("\n"), node) + addChild(LeafPsiElement(EOL_COMMENT, nodeText), node) + removeChild(node) + } + } else { + if (node.treePrev.isWhiteSpace()) { + node.treeParent.removeChild(node.treePrev) + } + val newLineNodeOnPreviousLine = node.findAllNodesWithConditionOnLine(node.getLineNumber() - 1) { + it.elementType == WHITE_SPACE && it.textContains('\n') + }?.lastOrNull() + newLineNodeOnPreviousLine?.let { + val parent = node.treeParent + parent.removeChild(node) + newLineNodeOnPreviousLine.treeParent.addChild(node, newLineNodeOnPreviousLine.treeNext) + newLineNodeOnPreviousLine.treeParent.addChild(PsiWhiteSpaceImpl("\n"), newLineNodeOnPreviousLine.treeNext.treeNext) + } + } + } + } /** * Class StringTemplate show that long line should be split in string template @@ -533,42 +518,250 @@ class LineLength(configRules: List) : DiktatRule( node: ASTNode, val delimiterIndex: Int, val isOneLineString: Boolean - ) : LongLineFixableCases(node) + ) : LongLineFixableCases(node) { + override fun fix() { + val incorrectText = node.text + val firstPart = incorrectText.substring(0, delimiterIndex) + val secondPart = incorrectText.substring(delimiterIndex, incorrectText.length) + val textBetweenParts = + if (isOneLineString) { + "\" +\n\"" + } else { + "\n" + } + val correctNode = KotlinParser().createNode("$firstPart$textBetweenParts$secondPart") + node.treeParent.replaceChild(node, correctNode) + } + } /** * Class BinaryExpression show that long line should be split in short binary expression? after operation reference */ - private class BinaryExpression(node: ASTNode) : LongLineFixableCases(node) + private class BinaryExpression(node: ASTNode) : LongLineFixableCases(node) { + override fun fix() { + val nodeOperationReference = node.findChildByType(OPERATION_REFERENCE) + val nextNode = if (nodeOperationReference?.firstChildNode?.elementType != ELVIS) { + nodeOperationReference?.treeNext + } else { + if (nodeOperationReference.treePrev.elementType == WHITE_SPACE) { + nodeOperationReference.treePrev + } else { + nodeOperationReference + } + } + node.appendNewlineMergingWhiteSpace(nextNode, nextNode) + } + } /** * Class LongBinaryExpression show that long line should be split between other parts long binary expression, * after one of operation reference - * @property maximumLineLength - * @property leftOffset - * @property binList + * @property maximumLineLength is number of maximum line length + * @property leftOffset is offset before start [node] + * @property binList is list of Binary Expression which are children of [node] + * @property positionByOffset */ private class LongBinaryExpression( node: ASTNode, val maximumLineLength: LineLengthConfiguration, val leftOffset: Int, - val binList: MutableList - ) : LongLineFixableCases(node) + val binList: MutableList, + var positionByOffset: (Int) -> Pair + ) : LongLineFixableCases(node) { + /** + * Fix a binary expression - + * If the transfer is done on the Elvis operator, then transfers it to a new line + * If not on the Elvis operator, then transfers it to a new line after the operation reference + */ + @Suppress("UnsafeCallOnNullableType") + override fun fix() { + val anySplitNode = searchSomeSplitInBinaryExpression(node, maximumLineLength) + val rightSplitNode = anySplitNode[0] ?: anySplitNode[1] ?: anySplitNode[2] + val nodeOperationReference = rightSplitNode?.first?.getFirstChildWithType(OPERATION_REFERENCE) + rightSplitNode?.let { + val nextNode = if (nodeOperationReference?.firstChildNode?.elementType != ELVIS) { + nodeOperationReference?.treeNext + } else { + if (nodeOperationReference.treePrev.elementType == WHITE_SPACE) { + nodeOperationReference.treePrev + } else { + nodeOperationReference + } + } + if (!nextNode?.text?.contains(("\n"))!!) { + rightSplitNode.first.appendNewlineMergingWhiteSpace(nextNode, nextNode) + } + } + } + + /** + * This method stored all the nodes that have BINARY_EXPRESSION or PREFIX_EXPRESSION element type. + * Return List of the Pair + * First elem in List - Logic Binary Expression (&& ||) + * Second elem in List - Comparison Binary Expression (> < == >= <= !=) + * Other types (Arithmetical and Bit operation) (+ - * / % >> << *= += -= /= %= ++ -- ! in !in etc) + */ + @Suppress("TYPE_ALIAS") + private fun searchSomeSplitInBinaryExpression(parent: ASTNode, configuration: LineLengthConfiguration): List?> { + val logicListOperationReference = listOf(OROR, ANDAND) + val compressionListOperationReference = listOf(GT, LT, EQEQ, GTEQ, LTEQ, EXCLEQ) + val binList: MutableList = mutableListOf() + searchBinaryExpression(parent, binList) + val rightBinList = binList.map { + it to positionByOffset(it.getFirstChildWithType(OPERATION_REFERENCE)?.startOffset ?: 0).second + } + .sortedBy { it.second } + .reversed() + val returnList: MutableList?> = mutableListOf() + addInSmartListBinExpression(returnList, rightBinList, logicListOperationReference, configuration) + addInSmartListBinExpression(returnList, rightBinList, compressionListOperationReference, configuration) + val expression = rightBinList.firstOrNull { (it, offset) -> + val binOperationReference = it.getFirstChildWithType(OPERATION_REFERENCE)?.firstChildNode?.elementType + offset + (it.getFirstChildWithType(OPERATION_REFERENCE)?.text?.length ?: 0) <= configuration.lineLength + 1 && + binOperationReference !in logicListOperationReference && binOperationReference !in compressionListOperationReference && binOperationReference != EXCL + } + returnList.add(expression) + return returnList + } + + private fun searchBinaryExpression(node: ASTNode, binList: MutableList) { + if (node.hasChildOfType(BINARY_EXPRESSION) || node.hasChildOfType(PARENTHESIZED) || node.hasChildOfType(POSTFIX_EXPRESSION)) { + node.getChildren(null) + .forEach { + searchBinaryExpression(it, binList) + } + } + if (node.elementType == BINARY_EXPRESSION) { + binList.add(node) + binList.add(node.treeParent.findChildByType(PREFIX_EXPRESSION) ?: return) + } + } + + /** + * Runs through the sorted list [rightBinList], finds its last element, the type of which is included in the set [typesList] and adds it in the list [returnList] + */ + @Suppress("TYPE_ALIAS") + private fun addInSmartListBinExpression( + returnList: MutableList?>, + rightBinList: List>, + typesList: List, + configuration: LineLengthConfiguration + ) { + val expression = rightBinList.firstOrNull { (it, offset) -> + val binOperationReference = it.getFirstChildWithType(OPERATION_REFERENCE) + offset + (it.getFirstChildWithType(OPERATION_REFERENCE)?.text?.length ?: 0) <= configuration.lineLength + 1 && + binOperationReference?.firstChildNode?.elementType in typesList + } + returnList.add(expression) + } + } + + /** + * Class FunAndProperty show that long line should be split in Fun Or Property: after EQ (between head and body this function) + */ + private class FunAndProperty(node: ASTNode) : LongLineFixableCases(node) { + override fun fix() { + node.appendNewlineMergingWhiteSpace(null, node.findChildByType(EQ)?.treeNext) + } + } + + /** + * Class Lambda show that long line should be split in Lambda: in space after [LBRACE] node and before [RBRACE] node + */ + private class Lambda(node: ASTNode) : LongLineFixableCases(node) { + /** + * Splits Lambda expressions - add splits lines, thereby making the lambda expression a separate line + */ + override fun fix() { + node.appendNewlineMergingWhiteSpace(node.findChildByType(LBRACE)?.treeNext, node.findChildByType(LBRACE)?.treeNext) + node.appendNewlineMergingWhiteSpace(node.findChildByType(RBRACE)?.treePrev, node.findChildByType(RBRACE)?.treePrev) + } + } /** - * Class Fun show that long line should be split in Fun: after EQ (between head and body this function) + * Class DotQualifiedExpression show that line should be split in DotQualifiedExpression */ - private class FunAndProperty(node: ASTNode) : LongLineFixableCases(node) + private class DotQualifiedExpression(node: ASTNode) : LongLineFixableCases(node) { + override fun fix() { + val dot = node.getFirstChildWithType(DOT) + val safeAccess = node.getFirstChildWithType(SAFE_ACCESS) + val splitNode = if ((dot?.startOffset ?: 0) > (safeAccess?.startOffset ?: 0)) { + dot + } else { + safeAccess + } + val nodeBeforeDot = splitNode?.treePrev + node.appendNewlineMergingWhiteSpace(nodeBeforeDot, splitNode) + } + } /** - * Class Lambda show that long line should be split in Comment: in space between two words + * Class ValueArgumentList show that line should be split in ValueArgumentList: + * @property maximumLineLength - max line length + * @property positionByOffset */ - private class Lambda(node: ASTNode) : LongLineFixableCases(node) + private class ValueArgumentList( + node: ASTNode, + val maximumLineLength: LineLengthConfiguration, + var positionByOffset: (Int) -> Pair + ) : LongLineFixableCases(node) { + override fun fix() { + val lineLength = maximumLineLength.lineLength + val offset = fixFirst() + val listComma = node.getAllChildrenWithType(COMMA).map { + it to positionByOffset(it.startOffset - offset).second + }.sortedBy { it.second } + var lineNumber = 1 + listComma.forEachIndexed { index, pair -> + if (pair.second >= lineNumber * lineLength) { + lineNumber++ + val commaSplit = if (index > 0) { + listComma[index - 1].first + } else { + pair.first + } + node.appendNewlineMergingWhiteSpace(commaSplit.treeNext, commaSplit.treeNext) + } + } + node.getFirstChildWithType(RPAR)?.let { + if (positionByOffset(it.treePrev.startOffset).second + it.treePrev.text.length - offset > lineLength * lineNumber && listComma.isNotEmpty()) { + listComma.last().first.let { + node.appendNewlineMergingWhiteSpace(it.treeNext, it.treeNext) + } + } + } + } + + private fun fixFirst(): Int { + val lineLength = maximumLineLength.lineLength + var startOffset = 0 + node.getFirstChildWithType(COMMA)?.let { + if (positionByOffset(it.startOffset).second > lineLength) { + node.appendNewlineMergingWhiteSpace(node.findChildByType(LPAR)?.treeNext, node.findChildByType(LPAR)?.treeNext) + node.appendNewlineMergingWhiteSpace(node.findChildByType(RPAR), node.findChildByType(RPAR)) + startOffset = this.maximumLineLength.lineLength.toInt() + } + } ?: node.getFirstChildWithType(RPAR)?.let { + node.appendNewlineMergingWhiteSpace(node.findChildByType(LPAR)?.treeNext, node.findChildByType(LPAR)?.treeNext) + node.appendNewlineMergingWhiteSpace(node.findChildByType(RPAR), node.findChildByType(RPAR)) + startOffset = this.maximumLineLength.lineLength.toInt() + } + return startOffset + } + } /** - * Class Property show that long line should be split in property: after a EQ - * @property indexLastSpace - * @property text + * Class WhenEntry show that line should be split in WhenEntry node: + * Added [LBRACE] and [RBRACE] nodes + * Split line in space after [LBRACE] node and before [RBRACE] node */ + private class WhenEntry(node: ASTNode) : LongLineFixableCases(node) { + override fun fix() { + node.getFirstChildWithType(ARROW)?.let { + node.appendNewlineMergingWhiteSpace(it.treeNext, it.treeNext) + } + } + } /** * val text = "first part" + 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..257d2710cc 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 @@ -88,4 +88,14 @@ 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) + } + + @Test + fun `fix long value arguments list`() { + fixAndCompare("LongValueArgumentsListExpected.kt", "LongValueArgumentsListTest.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 c4061e15fb..144d3656a7 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/kotlin/org/cqfn/diktat/util/SuppressingTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/SuppressingTest.kt index e9bf4eea1b..1c0ad14c66 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/SuppressingTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/SuppressingTest.kt @@ -1,26 +1,11 @@ package org.cqfn.diktat.util import org.cqfn.diktat.common.config.rules.RulesConfig -import org.cqfn.diktat.ruleset.constants.Warnings.BACKTICKS_PROHIBITED -import org.cqfn.diktat.ruleset.constants.Warnings.CLASS_NAME_INCORRECT -import org.cqfn.diktat.ruleset.constants.Warnings.CONFUSING_IDENTIFIER_NAMING -import org.cqfn.diktat.ruleset.constants.Warnings.CONSTANT_UPPERCASE -import org.cqfn.diktat.ruleset.constants.Warnings.ENUM_VALUE -import org.cqfn.diktat.ruleset.constants.Warnings.EXCEPTION_SUFFIX -import org.cqfn.diktat.ruleset.constants.Warnings.FUNCTION_BOOLEAN_PREFIX -import org.cqfn.diktat.ruleset.constants.Warnings.GENERIC_NAME import org.cqfn.diktat.ruleset.constants.Warnings.IDENTIFIER_LENGTH -import org.cqfn.diktat.ruleset.constants.Warnings.OBJECT_NAME_INCORRECT -import org.cqfn.diktat.ruleset.constants.Warnings.VARIABLE_HAS_PREFIX -import org.cqfn.diktat.ruleset.constants.Warnings.VARIABLE_NAME_INCORRECT -import org.cqfn.diktat.ruleset.constants.Warnings.VARIABLE_NAME_INCORRECT_FORMAT import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID import org.cqfn.diktat.ruleset.rules.chapter1.IdentifierNaming import com.pinterest.ktlint.core.LintError -import generated.WarningNames -import org.junit.jupiter.api.Tag -import org.junit.jupiter.api.Tags import org.junit.jupiter.api.Test class SuppressingTest : LintTestBase(::IdentifierNaming) { 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..ab9766e750 --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongDotQualifiedExpressionExpected.kt @@ -0,0 +1,22 @@ +package test.paragraph3.long_line + +val G = + ThisIsVeryyyyLooooonNameDooootQualifiedExpressioWithoutDot.lalalala.lalalal + +val A = This.Is.Veeeeryyyyyyy.Loooooong.Dot +.Qualified.Expression + +val B = This?.Is?.Veeeeryyyyyyy?.Loooooong?.Dot +?.Qualified?.Expression + +val C = This!!.Is!!.Veeeeryyyyyyy!!.Loooooong!! +.Dot!!.Qualified!!.Expression + +val D = This.Is.Veeeeryyyyyyy.Loooooong.Dot + .Qualified.Expression + +val E = This?.Is?.Veeeeryyyyyyy?.Loooooong?.Dot + ?.Qualified?.Expression + +val F = This!!.Is!!.Veeeeryyyyyyy!!.Loooooong!! + .Dot!!.Qualified!!.Expression diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongDotQualifiedExpressionTest.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongDotQualifiedExpressionTest.kt new file mode 100644 index 0000000000..9bbde9b17c --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongDotQualifiedExpressionTest.kt @@ -0,0 +1,18 @@ +package test.paragraph3.long_line + +val G = ThisIsVeryyyyLooooonNameDooootQualifiedExpressioWithoutDot.lalalala.lalalal + +val A = This.Is.Veeeeryyyyyyy.Loooooong.Dot.Qualified.Expression + +val B = This?.Is?.Veeeeryyyyyyy?.Loooooong?.Dot?.Qualified?.Expression + +val C = This!!.Is!!.Veeeeryyyyyyy!!.Loooooong!!.Dot!!.Qualified!!.Expression + +val D = This.Is.Veeeeryyyyyyy.Loooooong.Dot + .Qualified.Expression + +val E = This?.Is?.Veeeeryyyyyyy?.Loooooong?.Dot + ?.Qualified?.Expression + +val F = This!!.Is!!.Veeeeryyyyyyy!!.Loooooong!! + .Dot!!.Qualified!!.Expression diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongExpressionNoFixExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongExpressionNoFixExpected.kt index ddc30f0694..22e030d248 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongExpressionNoFixExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongExpressionNoFixExpected.kt @@ -3,6 +3,6 @@ package org.cqfn.diktat.resources.paragraph3.longline class veryLoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong { // looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongggggggggggggggggggggggggg //looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooongggggggggggggggggggggggggg - val s = "d" + -" s d d d d ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + val s = + "d s d d d d ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" } diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongInlineCommentsExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongInlineCommentsExpected.kt index 4e9bf7519b..789a4a066e 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongInlineCommentsExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongInlineCommentsExpected.kt @@ -45,7 +45,8 @@ fun foo() { // ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ElementType.IF -> handleIfElse(node) ElementType.EOL_COMMENT, ElementType.BLOCK_COMMENT -> handleEolAndBlockComments(node, configuration) - ElementType.KDOC -> handleKdocComments(node, configuration) + ElementType.KDOC -> + handleKdocComments(node, configuration) // ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff else -> { // this is a generated else block 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..3170f8c26a 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,20 +1,28 @@ 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) +@Query(value = "select * from test inner joi", + nativeQuery = true) fun test(limit: Int, offset: Int, executionId: Long): List -@Query(value = "select * from test inner joibbb", nativeQuery = true) +@Query(value = "select * from test inner joibbb", + nativeQuery = true) fun cornerCase(limit: Int, offset: Int, executionId: Long): List -@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) = println("testtesttesttesttesttesttesttesttesttesttesttest") 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 04cf2b52f5..a92a4ca7e5 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,10 +7,11 @@ fun foo() { fun foo() { val veryLoooooooooooooooooongNamesList = listOf("Jack", "Nick") - veryLoooooooooooooooooongNamesList.forEach { - name -> + veryLoooooooooooooooooongNamesList +.forEach { name -> if (name == "Nick") { - veryLoooooooooooooooooongNamesList.map { val str = "This string shouldn't be split"} + veryLoooooooooooooooooongNamesList +.map { val str = "This string shouldn't be split"} name.map { val str = "This string should be split" } } @@ -32,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 diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongStringTemplateExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongStringTemplateExpected.kt index b386956da8..63ce3b3c8f 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongStringTemplateExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongStringTemplateExpected.kt @@ -3,10 +3,12 @@ object Observables { val someCode = 15 // Some // looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line - @Deprecated("New type inference algorithm in Kotlin 1.4 makes this method obsolete. Method will be removed in" + -" future RxKotlin release.", - replaceWith = ReplaceWith("Observable.combineLatest(source1, source2, source3, source4, combineFunction)", "io.reactivex.Observable"), - level = DeprecationLevel.WARNING) + @Deprecated( +"New type inference algorithm in Kotlin 1.4 makes this method obsolete. Method will be removed in future RxKotlin release.", + replaceWith = ReplaceWith("Observable.combineLatest(source1, source2, source3, source4, combineFunction)", + "io.reactivex.Observable"), + level = DeprecationLevel.WARNING +) @CheckReturnValue @SchedulerSupport(SchedulerSupport.NONE) inline fun combineLatest() {} @@ -15,9 +17,12 @@ val someCode = 15 class Foo() { fun Fuu() { - logger.log("<-- ${response.code} ${ if (response.message.isEmpty()) "skfnvkdjdfvd" else "dfjvndkjnbvif" + - response.message}") - logger.log("<-- ${response.code} ${ if (response.message.isEmpty()) "skfnvsdcsdcscskdjdfvd" else "dfjvndsdcsdcsdcskjnbvif" + response.message}") + logger.log( +"<-- ${response.code} ${ if (response.message.isEmpty()) "skfnvkdjdfvd" else "dfjvndkjnbvif" + response.message}" +) + logger.log( +"<-- ${response.code} ${ if (response.message.isEmpty()) "skfnvsdcsdcscskdjdfvd" else "dfjvndsdcsdcsdcskjnbvif" + response.message}" +) } val q = """ @@ -46,4 +51,7 @@ class Foo() { } """.trimIndent() } + + val stringName = "This is long string template with binary expression. test should be up in level binary" + +" expression and cannot split in operation reference and should be split this long string template" + "this string should be after operated reference" } diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongStringTemplateTest.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongStringTemplateTest.kt index 005cf1a91c..92188058d9 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongStringTemplateTest.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongStringTemplateTest.kt @@ -38,4 +38,6 @@ class Foo() { } """.trimIndent() } + + val stringName = "This is long string template with binary expression. test should be up in level binary expression and cannot split in operation reference and should be split this long string template" + "this string should be after operated reference" } diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongValueArgumentsListExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongValueArgumentsListExpected.kt new file mode 100644 index 0000000000..bcb4040527 --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongValueArgumentsListExpected.kt @@ -0,0 +1,32 @@ +package test.paragraph3.long_line + +val firstArgument = 1 +val secondArgument = 2 +val thirdArgument = 3 +val fourthArgument = 4 +val fifthArguments = 5 +val sixthArguments = 6 +val seventhArguments = 7 +val eighthArguments = 8 + +// Many arguments in function +val result1 = ManyParamInFunction(firstArgument, + secondArgument, thirdArgument, fourthArgument, + fifthArguments, sixthArguments, seventhArguments, + eighthArguments) + +// +val result2 = veryLongNameFun(firstArgument, + secondArgument) + +// first argument cannot to be able to stay in +// the first line +val result3 = veryLongNameInFirstParam( +firstArgument, secondArgument, thirdArgument +) + +// first argument cannot to be able to stay in +// the first line +val result4 = veryLongNameInFirstParam( +firstArgument +) \ No newline at end of file diff --git a/diktat-rules/src/test/resources/test/paragraph3/long_line/LongValueArgumentsListTest.kt b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongValueArgumentsListTest.kt new file mode 100644 index 0000000000..d516f45726 --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph3/long_line/LongValueArgumentsListTest.kt @@ -0,0 +1,22 @@ +package test.paragraph3.long_line + +val firstArgument = 1 +val secondArgument = 2 +val thirdArgument = 3 +val fourthArgument = 4 +val fifthArguments = 5 +val sixthArguments = 6 +val seventhArguments = 7 +val eighthArguments = 8 + +// Many arguments in function +val result1 = ManyParamInFunction(firstArgument, secondArgument, thirdArgument, fourthArgument, fifthArguments, sixthArguments, seventhArguments, eighthArguments) + +// +val result2 = veryLongNameFun(firstArgument, secondArgument) + +// first argument cannot to be able to stay in the first line +val result3 = veryLongNameInFirstParam(firstArgument, secondArgument, thirdArgument) + +// first argument cannot to be able to stay in the first line +val result4 = veryLongNameInFirstParam(firstArgument) \ No newline at end of file diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/CheckLongLineExpected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/CheckLongLineExpected.kt new file mode 100644 index 0000000000..c7f575b940 --- /dev/null +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/CheckLongLineExpected.kt @@ -0,0 +1,3 @@ +package org.cqfn.diktat + +val A = This.Is.Veeeeryyyyyyy.Loooooong.Dot.Qualified.Expression diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/CheckLongLineTest.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/CheckLongLineTest.kt new file mode 100644 index 0000000000..92665f2891 --- /dev/null +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/CheckLongLineTest.kt @@ -0,0 +1,6 @@ +package org.cqfn.diktat + +/** + * @return + */ +val A = This.Is.Veeeeryyyyyyy.Loooooong.Dot.Qualified.Expression \ No newline at end of file