From 60bbcd40111d3ab205e01b4e45911a74e8a3c209 Mon Sep 17 00:00:00 2001 From: kentr0w Date: Wed, 30 Dec 2020 01:03:19 +0300 Subject: [PATCH 1/9] New rule for complex expression ### What's done: Created new rule --- diktat-analysis.yml | 2 ++ .../src/main/kotlin/generated/WarningNames.kt | 2 ++ .../cqfn/diktat/ruleset/constants/Warnings.kt | 1 + .../ruleset/rules/files/NewlinesRule.kt | 31 ++++++++++++++----- .../main/resources/diktat-analysis-huawei.yml | 2 ++ .../src/main/resources/diktat-analysis.yml | 2 ++ .../chapter3/files/NewlinesRuleWarnTest.kt | 26 ++++++++++++++++ info/available-rules.md | 1 + 8 files changed, 59 insertions(+), 8 deletions(-) diff --git a/diktat-analysis.yml b/diktat-analysis.yml index 38a03d1f58..bc87fbf884 100644 --- a/diktat-analysis.yml +++ b/diktat-analysis.yml @@ -147,6 +147,8 @@ enabled: true configuration: maxParametersInOneLine: 2 +- name: COMPLEX_EXPRESSION + enabled: true - name: TOO_MANY_CONSECUTIVE_SPACES enabled: true configuration: diff --git a/diktat-rules/src/main/kotlin/generated/WarningNames.kt b/diktat-rules/src/main/kotlin/generated/WarningNames.kt index 10842cf258..8012b15334 100644 --- a/diktat-rules/src/main/kotlin/generated/WarningNames.kt +++ b/diktat-rules/src/main/kotlin/generated/WarningNames.kt @@ -143,6 +143,8 @@ public object WarningNames { public const val WRONG_NEWLINES: String = "WRONG_NEWLINES" + public const val COMPLEX_EXPRESSION: String = "COMPLEX_EXPRESSION" + public const val STRING_CONCATENATION: String = "STRING_CONCATENATION" public const val TOO_MANY_BLANK_LINES: String = "TOO_MANY_BLANK_LINES" diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt index 156d662dae..3e748ecd19 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt @@ -96,6 +96,7 @@ enum class Warnings( LONG_LINE(true, "3.5.1", "this line is longer than allowed"), REDUNDANT_SEMICOLON(true, "3.6.2", "there should be no redundant semicolon at the end of lines"), WRONG_NEWLINES(true, "3.6.2", "incorrect line breaking"), + COMPLEX_EXPRESSION(false, "3.6.3", "complex dot qualified expression can be replace with variable"), // FixMe: autofixing will be added for this rule STRING_CONCATENATION(false, "3.15.1", "strings should not be concatenated using plus operator - use string templates instead if the statement fits one line"), diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt index d08f1f742b..db17050ad4 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt @@ -7,6 +7,7 @@ import org.cqfn.diktat.ruleset.constants.EmitType import org.cqfn.diktat.ruleset.constants.ListOfList import org.cqfn.diktat.ruleset.constants.Warnings.REDUNDANT_SEMICOLON import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_NEWLINES +import org.cqfn.diktat.ruleset.constants.Warnings.COMPLEX_EXPRESSION import org.cqfn.diktat.ruleset.utils.* import com.pinterest.ktlint.core.Rule @@ -18,10 +19,10 @@ import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT import com.pinterest.ktlint.core.ast.ElementType.CALLABLE_REFERENCE_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.CALL_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.CLASS -import com.pinterest.ktlint.core.ast.ElementType.CLASS_BODY import com.pinterest.ktlint.core.ast.ElementType.COLON import com.pinterest.ktlint.core.ast.ElementType.COLONCOLON import com.pinterest.ktlint.core.ast.ElementType.COMMA +import com.pinterest.ktlint.core.ast.ElementType.CONDITION import com.pinterest.ktlint.core.ast.ElementType.DIV import com.pinterest.ktlint.core.ast.ElementType.DIVEQ import com.pinterest.ktlint.core.ast.ElementType.DOT @@ -51,7 +52,6 @@ import com.pinterest.ktlint.core.ast.ElementType.PLUS import com.pinterest.ktlint.core.ast.ElementType.PLUSEQ import com.pinterest.ktlint.core.ast.ElementType.POSTFIX_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.PRIMARY_CONSTRUCTOR -import com.pinterest.ktlint.core.ast.ElementType.RBRACE import com.pinterest.ktlint.core.ast.ElementType.RETURN import com.pinterest.ktlint.core.ast.ElementType.RETURN_KEYWORD import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS @@ -59,11 +59,12 @@ import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.SECONDARY_CONSTRUCTOR import com.pinterest.ktlint.core.ast.ElementType.SEMICOLON import com.pinterest.ktlint.core.ast.ElementType.SUPER_TYPE_LIST +import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT import com.pinterest.ktlint.core.ast.ElementType.VALUE_ARGUMENT_LIST import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER_LIST +import com.pinterest.ktlint.core.ast.ElementType.WHEN 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.nextCodeSibling import com.pinterest.ktlint.core.ast.parent @@ -190,6 +191,9 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" } } } + if (node.isInBrackets()) { + COMPLEX_EXPRESSION.warn(configRules, emitWarn, isFixMode, node.text, node.startOffset, node) + } } } @@ -375,11 +379,11 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" if (psi.children.isNotEmpty() && (!psi.isFirstChildElementType(DOT_QUALIFIED_EXPRESSION) && !psi.isFirstChildElementType(SAFE_ACCESS_EXPRESSION))) { val firstChild = psi.firstChild + if (firstChild.isFirstChildElementType(DOT_QUALIFIED_EXPRESSION) || + firstChild.isFirstChildElementType(SAFE_ACCESS_EXPRESSION)) { + getOrderedCallExpressions(firstChild.firstChild, result) + } if (firstChild.isFirstChildElementType(POSTFIX_EXPRESSION)) { - if (firstChild.isFirstChildElementType(DOT_QUALIFIED_EXPRESSION) || - firstChild.isFirstChildElementType(SAFE_ACCESS_EXPRESSION)) { - getOrderedCallExpressions(firstChild.firstChild, result) - } result.add(firstChild.node) } result.add(firstChild.node @@ -449,7 +453,7 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" } } // fixme: we can't distinguish fully qualified names (like java.lang) from chain of property accesses (like list.size) for now - ?.dropWhile { !it.treeParent.textContains('(') && !it.treeParent.textContains('{') } + //?.dropWhile { !it.treeParent.textContains('(') && !it.treeParent.textContains('{') } private fun List.isNotValidCalls(node: ASTNode): Boolean { if (this.size == 1) { @@ -504,6 +508,16 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" firstChildNode.elementType == IDENTIFIER && treeParent.elementType == BINARY_EXPRESSION + /** + * This method checks that complex expression should be replace with new variable + */ + private fun ASTNode.isInBrackets() = + parent({it.elementType == DOT_QUALIFIED_EXPRESSION || it.elementType == SAFE_ACCESS_EXPRESSION}) + ?.treeParent + ?.elementType + ?.let { it in bracketsTypes } + ?: false + /** * [RuleConfiguration] for newlines placement */ @@ -527,5 +541,6 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" private val expressionTypes = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION, CALLABLE_REFERENCE_EXPRESSION, BINARY_EXPRESSION) private val chainExpressionTypes = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION) private val dropChainValues = TokenSet.create(EOL_COMMENT, WHITE_SPACE, BLOCK_COMMENT, KDOC) + private val bracketsTypes = TokenSet.create(CONDITION, WHEN ,VALUE_ARGUMENT) } } diff --git a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml index 3d8614fc42..2094094755 100644 --- a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml +++ b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml @@ -145,6 +145,8 @@ enabled: true configuration: maxParametersInOneLine: 2 +- name: COMPLEX_EXPRESSION + enabled: true - name: TOO_MANY_CONSECUTIVE_SPACES enabled: true configuration: diff --git a/diktat-rules/src/main/resources/diktat-analysis.yml b/diktat-rules/src/main/resources/diktat-analysis.yml index a043d1d4d5..1059356115 100644 --- a/diktat-rules/src/main/resources/diktat-analysis.yml +++ b/diktat-rules/src/main/resources/diktat-analysis.yml @@ -147,6 +147,8 @@ enabled: true configuration: maxParametersInOneLine: 2 +- name: COMPLEX_EXPRESSION + enabled: true - name: TOO_MANY_CONSECUTIVE_SPACES enabled: true configuration: diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt index 05878c2f7c..c0d7e3e5cf 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt @@ -1,6 +1,7 @@ package org.cqfn.diktat.ruleset.chapter3.files import org.cqfn.diktat.common.config.rules.RulesConfig +import org.cqfn.diktat.ruleset.constants.Warnings.COMPLEX_EXPRESSION import org.cqfn.diktat.ruleset.constants.Warnings.REDUNDANT_SEMICOLON import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_NEWLINES import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID @@ -11,6 +12,7 @@ import com.pinterest.ktlint.core.LintError import generated.WarningNames import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Tag +import org.junit.jupiter.api.Tags import org.junit.jupiter.api.Test @Suppress("LargeClass") @@ -955,4 +957,28 @@ class NewlinesRuleWarnTest : LintTestBase(::NewlinesRule) { rulesConfigList = rulesConfigListShort ) } + + @Test + @Tags(Tag(WarningNames.WRONG_NEWLINES), Tag(WarningNames.COMPLEX_EXPRESSION)) + fun `complex expression in condition`() { + lintMethod( + """ + |fun foo() { + | if(a.b.c) {} + | while(a?.b?.c) {} + | when(a.b!!.c) {} + | goo(a?.b.c) + |} + """.trimMargin(), + LintError(2, 10, ruleId, "$functionalStyleWarn .", true), + LintError(2, 10, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), + LintError(3, 14, ruleId, "$functionalStyleWarn ?.", true), + LintError(3, 14, ruleId, "${COMPLEX_EXPRESSION.warnText()} ?.", false), + LintError(4, 14, ruleId, "$functionalStyleWarn .", true), + LintError(4, 14, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), + LintError(5, 12, ruleId, "$functionalStyleWarn .", true), + LintError(5, 12, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), + rulesConfigList = rulesConfigListShort + ) + } } diff --git a/info/available-rules.md b/info/available-rules.md index 67142a34dd..7e2e9edb39 100644 --- a/info/available-rules.md +++ b/info/available-rules.md @@ -67,6 +67,7 @@ | 3 | (rec) 3.4 | LONG_LINE | Check: warns if length doesn't exceed the specified length | no | lineLength | handle json method in KDoc | | 3 | 3.6 | REDUNDANT_SEMICOLON | Check: warns if semicolons are used at the end of line.
Fix: removes semicolon. | yes | no | - | | 3 | 3.6 | WRONG_NEWLINES | Check: warns if line breaks do not follow code style guid.
Fix: fixes incorrect line breaks. | yes | no | - | +| 3 | 3.6 | COMPLEX_EXPRESSION | Check: warns if long dot expression using in condition or as arguments | no | no | - | | 3 | 3.8 | STRING_CONCATENATION | Check: warns if in a single line concatenation of strings is used | yes | no | - | | 3 | 3.6 | TOO_MANY_BLANK_LINES | Check: warns if blank lines are used placed incorrectly.
Fix: removes redundant blank lines. | yes | no | | | 3 | 3.7 | WRONG_WHITESPACE | Check: warns if usage of horizontal spaces violates code style guide.
Fix: fixes incorrect whitespaces. | yes | no | - | From 26b8b0fd7312a90a321712ef8832928cf5979354 Mon Sep 17 00:00:00 2001 From: kentr0w Date: Wed, 30 Dec 2020 01:25:52 +0300 Subject: [PATCH 2/9] New rule for complex expression ### What's done: Fixed documentation --- .../cqfn/diktat/ruleset/rules/files/NewlinesRule.kt | 6 +++--- .../ruleset/chapter3/files/NewlinesRuleWarnTest.kt | 8 ++++---- info/available-rules.md | 1 + info/guide/guide-chapter-3.md | 13 +++++++++++++ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt index db17050ad4..968fcb20a8 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt @@ -163,6 +163,9 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" val isIncorrect = (if (node.elementType == ELVIS) node.treeParent else node).run { if (isCallsChain()) { val isSingleLineIfElse = parent({ it.elementType == IF }, true)?.isSingleLineIfElse() ?: false + if (node.isInBrackets()) { + COMPLEX_EXPRESSION.warn(configRules, emitWarn, isFixMode, node.text, node.startOffset, node) + } // to follow functional style these operators should be started by newline (isFollowedByNewline() || !isBeginByNewline()) && !isSingleLineIfElse && (!isFirstCall() || !isMultilineLambda(treeParent)) @@ -191,9 +194,6 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" } } } - if (node.isInBrackets()) { - COMPLEX_EXPRESSION.warn(configRules, emitWarn, isFixMode, node.text, node.startOffset, node) - } } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt index c0d7e3e5cf..63bc4ff713 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt @@ -970,14 +970,14 @@ class NewlinesRuleWarnTest : LintTestBase(::NewlinesRule) { | goo(a?.b.c) |} """.trimMargin(), - LintError(2, 10, ruleId, "$functionalStyleWarn .", true), LintError(2, 10, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), - LintError(3, 14, ruleId, "$functionalStyleWarn ?.", true), + LintError(2, 10, ruleId, "$functionalStyleWarn .", true), LintError(3, 14, ruleId, "${COMPLEX_EXPRESSION.warnText()} ?.", false), - LintError(4, 14, ruleId, "$functionalStyleWarn .", true), + LintError(3, 14, ruleId, "$functionalStyleWarn ?.", true), LintError(4, 14, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), - LintError(5, 12, ruleId, "$functionalStyleWarn .", true), + LintError(4, 14, ruleId, "$functionalStyleWarn .", true), LintError(5, 12, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), + LintError(5, 12, ruleId, "$functionalStyleWarn .", true), rulesConfigList = rulesConfigListShort ) } diff --git a/info/available-rules.md b/info/available-rules.md index 085763b398..2e222ee8b6 100644 --- a/info/available-rules.md +++ b/info/available-rules.md @@ -67,6 +67,7 @@ | 3 | 3.5.1 | LONG_LINE | Check: warns if length doesn't exceed the specified length | no | lineLength | handle json method in KDoc | | 3 | 3.6.2 | REDUNDANT_SEMICOLON | Check: warns if semicolons are used at the end of line.
Fix: removes semicolon. | yes | no | - | | 3 | 3.6.2 | WRONG_NEWLINES | Check: warns if line breaks do not follow code style guid.
Fix: fixes incorrect line breaks. | yes | no | - | +| 3 | 3.6.2 | COMPLEX_EXPRESSION | Check: warns if long dot expression using in condition or as arguments | no | no | - | | 3 | 3.15.1 | STRING_CONCATENATION | Check: warns if in a single line concatenation of strings is used | yes | no | - | | 3 | 3.7.1 | TOO_MANY_BLANK_LINES | Check: warns if blank lines are used placed incorrectly.
Fix: removes redundant blank lines. | yes | no | | | 3 | 3.8.1 | WRONG_WHITESPACE | Check: warns if usage of horizontal spaces violates code style guide.
Fix: fixes incorrect whitespaces. | yes | no | - | diff --git a/info/guide/guide-chapter-3.md b/info/guide/guide-chapter-3.md index 5e47c6cdf9..14786b3fa4 100644 --- a/info/guide/guide-chapter-3.md +++ b/info/guide/guide-chapter-3.md @@ -366,6 +366,19 @@ Note that all comparison operators, such as `==`, `>`, `<`, should not be split. ```kotlin if (condition) list.map { foo(it) }.filter { bar(it) } else list.drop(1) ``` + +**Note:** If dot qualified expression is inside condition or passed as an argument - replace with new vatiable + +**Invalid example**: +```kotlin + if (node.text.length.dec() != 0) {} +``` + +**Valid example**: +```kotlin + val nodeLen = node.text.length.dec() + if (nodeLen != 0) {} +``` 2) Newlines should be placed after the assignment operator (`=`). 3) In function or class declarations, the name of a function or constructor should not be split by a newline from the opening brace `(`. From c884171bce36e9dde3183f85f25f9113d1f3547f Mon Sep 17 00:00:00 2001 From: kentr0w Date: Wed, 30 Dec 2020 01:25:52 +0300 Subject: [PATCH 3/9] New rule for complex expression ### What's done: Fixed documentation --- .../cqfn/diktat/ruleset/rules/files/NewlinesRule.kt | 7 ++++--- .../ruleset/chapter3/files/NewlinesRuleWarnTest.kt | 8 ++++---- info/available-rules.md | 1 + info/guide/guide-chapter-3.md | 13 +++++++++++++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt index db17050ad4..56c55dd6ed 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt @@ -93,6 +93,7 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings * 7. Ensures that in multiline lambda newline follows arrow or, in case of lambda without explicit parameters, opening brace * 8. Checks that functions with single `return` are simplified to functions with expression body * 9. parameter or argument lists and supertype lists that have more than 2 elements should be separated by newlines + * 10. Complex expression inside condition replaced with new variable */ @Suppress("ForbiddenComment") class NewlinesRule(private val configRules: List) : Rule("newlines") { @@ -163,6 +164,9 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" val isIncorrect = (if (node.elementType == ELVIS) node.treeParent else node).run { if (isCallsChain()) { val isSingleLineIfElse = parent({ it.elementType == IF }, true)?.isSingleLineIfElse() ?: false + if (node.isInBrackets()) { + COMPLEX_EXPRESSION.warn(configRules, emitWarn, isFixMode, node.text, node.startOffset, node) + } // to follow functional style these operators should be started by newline (isFollowedByNewline() || !isBeginByNewline()) && !isSingleLineIfElse && (!isFirstCall() || !isMultilineLambda(treeParent)) @@ -191,9 +195,6 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" } } } - if (node.isInBrackets()) { - COMPLEX_EXPRESSION.warn(configRules, emitWarn, isFixMode, node.text, node.startOffset, node) - } } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt index c0d7e3e5cf..63bc4ff713 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt @@ -970,14 +970,14 @@ class NewlinesRuleWarnTest : LintTestBase(::NewlinesRule) { | goo(a?.b.c) |} """.trimMargin(), - LintError(2, 10, ruleId, "$functionalStyleWarn .", true), LintError(2, 10, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), - LintError(3, 14, ruleId, "$functionalStyleWarn ?.", true), + LintError(2, 10, ruleId, "$functionalStyleWarn .", true), LintError(3, 14, ruleId, "${COMPLEX_EXPRESSION.warnText()} ?.", false), - LintError(4, 14, ruleId, "$functionalStyleWarn .", true), + LintError(3, 14, ruleId, "$functionalStyleWarn ?.", true), LintError(4, 14, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), - LintError(5, 12, ruleId, "$functionalStyleWarn .", true), + LintError(4, 14, ruleId, "$functionalStyleWarn .", true), LintError(5, 12, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), + LintError(5, 12, ruleId, "$functionalStyleWarn .", true), rulesConfigList = rulesConfigListShort ) } diff --git a/info/available-rules.md b/info/available-rules.md index 085763b398..2e222ee8b6 100644 --- a/info/available-rules.md +++ b/info/available-rules.md @@ -67,6 +67,7 @@ | 3 | 3.5.1 | LONG_LINE | Check: warns if length doesn't exceed the specified length | no | lineLength | handle json method in KDoc | | 3 | 3.6.2 | REDUNDANT_SEMICOLON | Check: warns if semicolons are used at the end of line.
Fix: removes semicolon. | yes | no | - | | 3 | 3.6.2 | WRONG_NEWLINES | Check: warns if line breaks do not follow code style guid.
Fix: fixes incorrect line breaks. | yes | no | - | +| 3 | 3.6.2 | COMPLEX_EXPRESSION | Check: warns if long dot expression using in condition or as arguments | no | no | - | | 3 | 3.15.1 | STRING_CONCATENATION | Check: warns if in a single line concatenation of strings is used | yes | no | - | | 3 | 3.7.1 | TOO_MANY_BLANK_LINES | Check: warns if blank lines are used placed incorrectly.
Fix: removes redundant blank lines. | yes | no | | | 3 | 3.8.1 | WRONG_WHITESPACE | Check: warns if usage of horizontal spaces violates code style guide.
Fix: fixes incorrect whitespaces. | yes | no | - | diff --git a/info/guide/guide-chapter-3.md b/info/guide/guide-chapter-3.md index 5e47c6cdf9..14786b3fa4 100644 --- a/info/guide/guide-chapter-3.md +++ b/info/guide/guide-chapter-3.md @@ -366,6 +366,19 @@ Note that all comparison operators, such as `==`, `>`, `<`, should not be split. ```kotlin if (condition) list.map { foo(it) }.filter { bar(it) } else list.drop(1) ``` + +**Note:** If dot qualified expression is inside condition or passed as an argument - replace with new vatiable + +**Invalid example**: +```kotlin + if (node.text.length.dec() != 0) {} +``` + +**Valid example**: +```kotlin + val nodeLen = node.text.length.dec() + if (nodeLen != 0) {} +``` 2) Newlines should be placed after the assignment operator (`=`). 3) In function or class declarations, the name of a function or constructor should not be split by a newline from the opening brace `(`. From 1ce31d6c87c69de6612bd7874b3a406681e3f9e0 Mon Sep 17 00:00:00 2001 From: kentr0w Date: Sun, 10 Jan 2021 23:52:15 +0300 Subject: [PATCH 4/9] New rule for complex expression ### What's done: Fixed after review --- diktat-analysis.yml | 1 + .../cqfn/diktat/ruleset/constants/Warnings.kt | 2 +- .../ruleset/rules/files/NewlinesRule.kt | 32 +++++++++++-------- .../main/resources/diktat-analysis-huawei.yml | 1 + .../src/main/resources/diktat-analysis.yml | 1 + .../chapter3/files/NewlinesRuleWarnTest.kt | 6 ++-- info/available-rules.md | 2 +- info/guide/guide-chapter-3.md | 2 +- 8 files changed, 27 insertions(+), 20 deletions(-) diff --git a/diktat-analysis.yml b/diktat-analysis.yml index bc87fbf884..019100be51 100644 --- a/diktat-analysis.yml +++ b/diktat-analysis.yml @@ -147,6 +147,7 @@ enabled: true configuration: maxParametersInOneLine: 2 +# Inspection that checks if a long dot qualified expression is used in condition or as an argument - name: COMPLEX_EXPRESSION enabled: true - name: TOO_MANY_CONSECUTIVE_SPACES diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt index 3e748ecd19..e6f29ee575 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt @@ -96,7 +96,7 @@ enum class Warnings( LONG_LINE(true, "3.5.1", "this line is longer than allowed"), REDUNDANT_SEMICOLON(true, "3.6.2", "there should be no redundant semicolon at the end of lines"), WRONG_NEWLINES(true, "3.6.2", "incorrect line breaking"), - COMPLEX_EXPRESSION(false, "3.6.3", "complex dot qualified expression can be replace with variable"), + COMPLEX_EXPRESSION(false, "3.6.3", "complex dot qualified expression should be replaced with variable"), // FixMe: autofixing will be added for this rule STRING_CONCATENATION(false, "3.15.1", "strings should not be concatenated using plus operator - use string templates instead if the statement fits one line"), diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt index 56c55dd6ed..2bacf32113 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt @@ -164,13 +164,13 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" val isIncorrect = (if (node.elementType == ELVIS) node.treeParent else node).run { if (isCallsChain()) { val isSingleLineIfElse = parent({ it.elementType == IF }, true)?.isSingleLineIfElse() ?: false - if (node.isInBrackets()) { - COMPLEX_EXPRESSION.warn(configRules, emitWarn, isFixMode, node.text, node.startOffset, node) - } // to follow functional style these operators should be started by newline (isFollowedByNewline() || !isBeginByNewline()) && !isSingleLineIfElse && (!isFirstCall() || !isMultilineLambda(treeParent)) } else { + if (isCallsChain(false) && node.isInParentheses()) { + COMPLEX_EXPRESSION.warn(configRules, emitWarn, isFixMode, node.text, node.startOffset, node) + } // unless statement is simple and on single line, these operators cannot have newline after isFollowedByNewline() && !isSingleDotStatementOnSingleLine() } @@ -444,17 +444,23 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" * * @return true - if there is error, and false if there is no error */ - private fun ASTNode.isCallsChain() = getCallChain()?.isNotValidCalls(this) ?: false - - private fun ASTNode.getCallChain() = getParentExpressions() - .lastOrNull() - ?.run { - mutableListOf().also { - getOrderedCallExpressions(psi, it) + private fun ASTNode.isCallsChain(isWithoutParentheses: Boolean = true) = getCallChain(isWithoutParentheses)?.isNotValidCalls(this) ?: false + + private fun ASTNode.getCallChain(isWithoutParentheses: Boolean = true): List? { + val parentExpressionList = getParentExpressions() + .lastOrNull() + ?.run { + mutableListOf().also { + getOrderedCallExpressions(psi, it) + } } + return if (isWithoutParentheses) + // fixme: we can't distinguish fully qualified names (like java.lang) from chain of property accesses (like list.size) for now + parentExpressionList?.dropWhile { !it.treeParent.textContains('(') && !it.treeParent.textContains('{') } + else { + parentExpressionList } - // fixme: we can't distinguish fully qualified names (like java.lang) from chain of property accesses (like list.size) for now - //?.dropWhile { !it.treeParent.textContains('(') && !it.treeParent.textContains('{') } + } private fun List.isNotValidCalls(node: ASTNode): Boolean { if (this.size == 1) { @@ -512,7 +518,7 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" /** * This method checks that complex expression should be replace with new variable */ - private fun ASTNode.isInBrackets() = + private fun ASTNode.isInParentheses() = parent({it.elementType == DOT_QUALIFIED_EXPRESSION || it.elementType == SAFE_ACCESS_EXPRESSION}) ?.treeParent ?.elementType diff --git a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml index 2094094755..fb15b84707 100644 --- a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml +++ b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml @@ -145,6 +145,7 @@ enabled: true configuration: maxParametersInOneLine: 2 +# Inspection that checks if a long dot qualified expression is used in condition or as an argument - name: COMPLEX_EXPRESSION enabled: true - name: TOO_MANY_CONSECUTIVE_SPACES diff --git a/diktat-rules/src/main/resources/diktat-analysis.yml b/diktat-rules/src/main/resources/diktat-analysis.yml index 1059356115..45f9c86a5b 100644 --- a/diktat-rules/src/main/resources/diktat-analysis.yml +++ b/diktat-rules/src/main/resources/diktat-analysis.yml @@ -147,6 +147,7 @@ enabled: true configuration: maxParametersInOneLine: 2 +# Inspection that checks if a long dot qualified expression is used in condition or as an argument - name: COMPLEX_EXPRESSION enabled: true - name: TOO_MANY_CONSECUTIVE_SPACES diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt index 63bc4ff713..b4d8dbbd1c 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt @@ -9,7 +9,9 @@ import org.cqfn.diktat.ruleset.rules.files.NewlinesRule import org.cqfn.diktat.util.LintTestBase import com.pinterest.ktlint.core.LintError +import com.pinterest.ktlint.core.ast.ElementType import generated.WarningNames +import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Tags @@ -971,13 +973,9 @@ class NewlinesRuleWarnTest : LintTestBase(::NewlinesRule) { |} """.trimMargin(), LintError(2, 10, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), - LintError(2, 10, ruleId, "$functionalStyleWarn .", true), LintError(3, 14, ruleId, "${COMPLEX_EXPRESSION.warnText()} ?.", false), - LintError(3, 14, ruleId, "$functionalStyleWarn ?.", true), LintError(4, 14, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), - LintError(4, 14, ruleId, "$functionalStyleWarn .", true), LintError(5, 12, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), - LintError(5, 12, ruleId, "$functionalStyleWarn .", true), rulesConfigList = rulesConfigListShort ) } diff --git a/info/available-rules.md b/info/available-rules.md index 2e222ee8b6..891cef99d2 100644 --- a/info/available-rules.md +++ b/info/available-rules.md @@ -67,7 +67,7 @@ | 3 | 3.5.1 | LONG_LINE | Check: warns if length doesn't exceed the specified length | no | lineLength | handle json method in KDoc | | 3 | 3.6.2 | REDUNDANT_SEMICOLON | Check: warns if semicolons are used at the end of line.
Fix: removes semicolon. | yes | no | - | | 3 | 3.6.2 | WRONG_NEWLINES | Check: warns if line breaks do not follow code style guid.
Fix: fixes incorrect line breaks. | yes | no | - | -| 3 | 3.6.2 | COMPLEX_EXPRESSION | Check: warns if long dot expression using in condition or as arguments | no | no | - | +| 3 | 3.6.2 | COMPLEX_EXPRESSION | Check: warns if a long dot qualified expression is used in condition or as an argument | no | no | - | | 3 | 3.15.1 | STRING_CONCATENATION | Check: warns if in a single line concatenation of strings is used | yes | no | - | | 3 | 3.7.1 | TOO_MANY_BLANK_LINES | Check: warns if blank lines are used placed incorrectly.
Fix: removes redundant blank lines. | yes | no | | | 3 | 3.8.1 | WRONG_WHITESPACE | Check: warns if usage of horizontal spaces violates code style guide.
Fix: fixes incorrect whitespaces. | yes | no | - | diff --git a/info/guide/guide-chapter-3.md b/info/guide/guide-chapter-3.md index 14786b3fa4..916180e0ce 100644 --- a/info/guide/guide-chapter-3.md +++ b/info/guide/guide-chapter-3.md @@ -367,7 +367,7 @@ Note that all comparison operators, such as `==`, `>`, `<`, should not be split. if (condition) list.map { foo(it) }.filter { bar(it) } else list.drop(1) ``` -**Note:** If dot qualified expression is inside condition or passed as an argument - replace with new vatiable +**Note:** If dot qualified expression is inside condition or passed as an argument - replace with new variable **Invalid example**: ```kotlin From 46c639c5a6bb296eacb66b0221c0287c8914a3f3 Mon Sep 17 00:00:00 2001 From: kentr0w Date: Mon, 11 Jan 2021 00:02:16 +0300 Subject: [PATCH 5/9] New rule for complex expression ### What's done: Fixed after merged --- diktat-analysis.yml | 3 +++ diktat-rules/src/main/resources/diktat-analysis-huawei.yml | 3 +++ diktat-rules/src/main/resources/diktat-analysis.yml | 3 +++ 3 files changed, 9 insertions(+) diff --git a/diktat-analysis.yml b/diktat-analysis.yml index d6f4e18929..f5a74248de 100644 --- a/diktat-analysis.yml +++ b/diktat-analysis.yml @@ -222,6 +222,9 @@ maxParametersInOneLine: 2 # 3 by default. # maxCallsInOneLine: 3 +# Inspection that checks if a long dot qualified expression is used in condition or as an argument +- name: COMPLEX_EXPRESSION + enabled: true # Checks that there are not too many consecutive spaces in line - name: TOO_MANY_CONSECUTIVE_SPACES enabled: true diff --git a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml index 799177837d..5943f23c3c 100644 --- a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml +++ b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml @@ -230,6 +230,9 @@ maxSpaces: '1' # Whether formatting for enums should be kept without checking saveInitialFormattingForEnums: false +# Inspection that checks if a long dot qualified expression is used in condition or as an argument +- name: COMPLEX_EXPRESSION + enabled: true # Checks that blank lines are used correctly. # For example: triggers when there are too many blank lines between function declaration - name: TOO_MANY_BLANK_LINES diff --git a/diktat-rules/src/main/resources/diktat-analysis.yml b/diktat-rules/src/main/resources/diktat-analysis.yml index dd70b9560b..d9a91a2d81 100644 --- a/diktat-rules/src/main/resources/diktat-analysis.yml +++ b/diktat-rules/src/main/resources/diktat-analysis.yml @@ -227,6 +227,9 @@ maxSpaces: '1' # Whether formatting for enums should be kept without checking saveInitialFormattingForEnums: false +# Inspection that checks if a long dot qualified expression is used in condition or as an argument +- name: COMPLEX_EXPRESSION + enabled: true # Checks that blank lines are used correctly. # For example: triggers when there are too many blank lines between function declaration - name: TOO_MANY_BLANK_LINES From 89581c91c55fc4f19206dfcb41473078b37693d4 Mon Sep 17 00:00:00 2001 From: kentr0w Date: Mon, 11 Jan 2021 10:19:55 +0300 Subject: [PATCH 6/9] New rule for complex expression ### What's done: Fixed after fixed --- .../ruleset/rules/AnnotationNewLineRule.kt | 7 ++++++- .../diktat/ruleset/rules/NullableTypeRule.kt | 5 ++++- .../diktat/ruleset/rules/SmartCastRule.kt | 5 +++-- .../ruleset/rules/files/NewlinesRule.kt | 20 +++++++++---------- .../chapter3/files/NewlinesRuleWarnTest.kt | 2 -- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt index b337dd9520..48deae26be 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt @@ -68,7 +68,12 @@ class AnnotationNewLineRule(private val configRules: List) : Rule(" if (node == node.treeParent.getFirstChildWithType(node.elementType)) { // Current node is ANNOTATION_ENTRY. treeParent(ModifierList) -> treeParent(PRIMARY_CONSTRUCTOR) // Checks if there is a white space before grandparent node - if (node.treeParent.treeParent.treePrev.isWhiteSpace()) { + val isParentWhiteSpace = node + .treeParent + .treeParent + .treePrev + .isWhiteSpace() + if (isParentWhiteSpace) { (node.treeParent.treeParent.treePrev as LeafPsiElement).replaceWithText("\n") } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/NullableTypeRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/NullableTypeRule.kt index 6565093846..1317fdd8e5 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/NullableTypeRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/NullableTypeRule.kt @@ -80,7 +80,10 @@ class NullableTypeRule(private val configRules: List) : Rule("nulla @Suppress("UnsafeCallOnNullableType") private fun findFixableParam(node: ASTNode): FixedParam? { - val reference = node.findChildByType(TYPE_REFERENCE)!!.findChildByType(NULLABLE_TYPE)!!.findChildByType(USER_TYPE)?.findChildByType(REFERENCE_EXPRESSION) + val reference = node.findChildByType(TYPE_REFERENCE)!! + .findChildByType(NULLABLE_TYPE)!! + .findChildByType(USER_TYPE) + ?.findChildByType(REFERENCE_EXPRESSION) ?: return null return when (reference.text) { "Boolean" -> FixedParam(BOOLEAN_CONSTANT, TRUE_KEYWORD, "true") diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SmartCastRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SmartCastRule.kt index ad4a3fd533..60497b7e81 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SmartCastRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/SmartCastRule.kt @@ -91,13 +91,14 @@ class SmartCastRule(private val configRules: List) : Rule("smart-ca @Suppress("NestedBlockDepth", "TYPE_ALIAS") private fun handleGroups(groups: Map>) { groups.keys.forEach { - if (it.node.treeParent.text.contains(" is ")) { + val parentText = it.node.treeParent.text + if (parentText.contains(" is ")) { groups.getValue(it).forEach { asCall -> if (asCall.node.hasParent(THEN)) { raiseWarning(asCall.node) } } - } else if (it.node.treeParent.text.contains(" !is ")) { + } else if (parentText.contains(" !is ")) { groups.getValue(it).forEach { asCall -> if (asCall.node.hasParent(ELSE)) { raiseWarning(asCall.node) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt index 2bacf32113..2e616d7fe5 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt @@ -5,9 +5,9 @@ import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.getRuleConfig import org.cqfn.diktat.ruleset.constants.EmitType import org.cqfn.diktat.ruleset.constants.ListOfList +import org.cqfn.diktat.ruleset.constants.Warnings.COMPLEX_EXPRESSION import org.cqfn.diktat.ruleset.constants.Warnings.REDUNDANT_SEMICOLON import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_NEWLINES -import org.cqfn.diktat.ruleset.constants.Warnings.COMPLEX_EXPRESSION import org.cqfn.diktat.ruleset.utils.* import com.pinterest.ktlint.core.Rule @@ -156,7 +156,7 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" } } - @Suppress("ComplexMethod") + @Suppress("ComplexMethod", "TOO_LONG_FUNCTION") private fun handleOperatorWithLineBreakBefore(node: ASTNode) { if (node.isDotFromPackageOrImport()) { return @@ -454,9 +454,10 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" getOrderedCallExpressions(psi, it) } } - return if (isWithoutParentheses) + return if (isWithoutParentheses) { // fixme: we can't distinguish fully qualified names (like java.lang) from chain of property accesses (like list.size) for now parentExpressionList?.dropWhile { !it.treeParent.textContains('(') && !it.treeParent.textContains('{') } + } else { parentExpressionList } @@ -518,12 +519,11 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" /** * This method checks that complex expression should be replace with new variable */ - private fun ASTNode.isInParentheses() = - parent({it.elementType == DOT_QUALIFIED_EXPRESSION || it.elementType == SAFE_ACCESS_EXPRESSION}) - ?.treeParent - ?.elementType - ?.let { it in bracketsTypes } - ?: false + private fun ASTNode.isInParentheses() = parent({it.elementType == DOT_QUALIFIED_EXPRESSION || it.elementType == SAFE_ACCESS_EXPRESSION}) + ?.treeParent + ?.elementType + ?.let { it in bracketsTypes } + ?: false /** * [RuleConfiguration] for newlines placement @@ -548,6 +548,6 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" private val expressionTypes = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION, CALLABLE_REFERENCE_EXPRESSION, BINARY_EXPRESSION) private val chainExpressionTypes = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION) private val dropChainValues = TokenSet.create(EOL_COMMENT, WHITE_SPACE, BLOCK_COMMENT, KDOC) - private val bracketsTypes = TokenSet.create(CONDITION, WHEN ,VALUE_ARGUMENT) + private val bracketsTypes = TokenSet.create(CONDITION, WHEN , VALUE_ARGUMENT) } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt index b4d8dbbd1c..f89c6fa0de 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt @@ -9,9 +9,7 @@ import org.cqfn.diktat.ruleset.rules.files.NewlinesRule import org.cqfn.diktat.util.LintTestBase import com.pinterest.ktlint.core.LintError -import com.pinterest.ktlint.core.ast.ElementType import generated.WarningNames -import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Tags From 24c5e243697405cb6224c79fb002070de6a264ef Mon Sep 17 00:00:00 2001 From: kentr0w Date: Mon, 11 Jan 2021 10:34:51 +0300 Subject: [PATCH 7/9] New rule for complex expression ### What's done: Fixed according our code-style --- .../org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt index 2e616d7fe5..21faa67f6b 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt @@ -457,8 +457,7 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" return if (isWithoutParentheses) { // fixme: we can't distinguish fully qualified names (like java.lang) from chain of property accesses (like list.size) for now parentExpressionList?.dropWhile { !it.treeParent.textContains('(') && !it.treeParent.textContains('{') } - } - else { + } else { parentExpressionList } } @@ -548,6 +547,6 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" private val expressionTypes = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION, CALLABLE_REFERENCE_EXPRESSION, BINARY_EXPRESSION) private val chainExpressionTypes = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION) private val dropChainValues = TokenSet.create(EOL_COMMENT, WHITE_SPACE, BLOCK_COMMENT, KDOC) - private val bracketsTypes = TokenSet.create(CONDITION, WHEN , VALUE_ARGUMENT) + private val bracketsTypes = TokenSet.create(CONDITION, WHEN, VALUE_ARGUMENT) } } From 42b16d2d8e75a2b458d8f0961a5fa4a025855095 Mon Sep 17 00:00:00 2001 From: kentr0w Date: Mon, 11 Jan 2021 16:45:34 +0300 Subject: [PATCH 8/9] New rule for complex expression ### What's done: Fixed after review --- .../diktat/ruleset/rules/AnnotationNewLineRule.kt | 4 ++-- .../diktat/ruleset/rules/files/NewlinesRule.kt | 13 ++++++++----- .../chapter3/files/NewlinesRuleWarnTest.kt | 15 +++++++++++++++ info/guide/guide-chapter-3.md | 11 +++++++---- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt index 48deae26be..eccc3ab41c 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/AnnotationNewLineRule.kt @@ -68,12 +68,12 @@ class AnnotationNewLineRule(private val configRules: List) : Rule(" if (node == node.treeParent.getFirstChildWithType(node.elementType)) { // Current node is ANNOTATION_ENTRY. treeParent(ModifierList) -> treeParent(PRIMARY_CONSTRUCTOR) // Checks if there is a white space before grandparent node - val isParentWhiteSpace = node + val hasSpaceBeforeGrandparent = node .treeParent .treeParent .treePrev .isWhiteSpace() - if (isParentWhiteSpace) { + if (hasSpaceBeforeGrandparent) { (node.treeParent.treeParent.treePrev as LeafPsiElement).replaceWithText("\n") } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt index 21faa67f6b..988bcc03fe 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt @@ -163,6 +163,9 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" } val isIncorrect = (if (node.elementType == ELVIS) node.treeParent else node).run { if (isCallsChain()) { + if (node.isInParentheses()) { + COMPLEX_EXPRESSION.warn(configRules, emitWarn, isFixMode, node.text, node.startOffset, node) + } val isSingleLineIfElse = parent({ it.elementType == IF }, true)?.isSingleLineIfElse() ?: false // to follow functional style these operators should be started by newline (isFollowedByNewline() || !isBeginByNewline()) && !isSingleLineIfElse && @@ -444,9 +447,9 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" * * @return true - if there is error, and false if there is no error */ - private fun ASTNode.isCallsChain(isWithoutParentheses: Boolean = true) = getCallChain(isWithoutParentheses)?.isNotValidCalls(this) ?: false + private fun ASTNode.isCallsChain(dropLeadingBrackets: Boolean = true) = getCallChain(dropLeadingBrackets)?.isNotValidCalls(this) ?: false - private fun ASTNode.getCallChain(isWithoutParentheses: Boolean = true): List? { + private fun ASTNode.getCallChain(dropLeadingBrackets: Boolean = true): List? { val parentExpressionList = getParentExpressions() .lastOrNull() ?.run { @@ -454,7 +457,7 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" getOrderedCallExpressions(psi, it) } } - return if (isWithoutParentheses) { + return if (dropLeadingBrackets) { // fixme: we can't distinguish fully qualified names (like java.lang) from chain of property accesses (like list.size) for now parentExpressionList?.dropWhile { !it.treeParent.textContains('(') && !it.treeParent.textContains('{') } } else { @@ -521,7 +524,7 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" private fun ASTNode.isInParentheses() = parent({it.elementType == DOT_QUALIFIED_EXPRESSION || it.elementType == SAFE_ACCESS_EXPRESSION}) ?.treeParent ?.elementType - ?.let { it in bracketsTypes } + ?.let { it in parenthesesTypes } ?: false /** @@ -547,6 +550,6 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" private val expressionTypes = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION, CALLABLE_REFERENCE_EXPRESSION, BINARY_EXPRESSION) private val chainExpressionTypes = TokenSet.create(DOT_QUALIFIED_EXPRESSION, SAFE_ACCESS_EXPRESSION) private val dropChainValues = TokenSet.create(EOL_COMMENT, WHITE_SPACE, BLOCK_COMMENT, KDOC) - private val bracketsTypes = TokenSet.create(CONDITION, WHEN, VALUE_ARGUMENT) + private val parenthesesTypes = TokenSet.create(CONDITION, WHEN, VALUE_ARGUMENT) } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt index f89c6fa0de..32bb645457 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt @@ -977,4 +977,19 @@ class NewlinesRuleWarnTest : LintTestBase(::NewlinesRule) { rulesConfigList = rulesConfigListShort ) } + + @Test + @Tags(Tag(WarningNames.WRONG_NEWLINES), Tag(WarningNames.COMPLEX_EXPRESSION)) + fun `complex expression in condition with double warnings`() { + lintMethod( + """ + |fun foo() { + | if(a().b().c()) {} + |} + """.trimMargin(), + LintError(2, 14, ruleId, "${COMPLEX_EXPRESSION.warnText()} .", false), + LintError(2, 14, ruleId, "$functionalStyleWarn .", true), + rulesConfigList = rulesConfigListShort + ) + } } diff --git a/info/guide/guide-chapter-3.md b/info/guide/guide-chapter-3.md index 916180e0ce..ff20ad9c30 100644 --- a/info/guide/guide-chapter-3.md +++ b/info/guide/guide-chapter-3.md @@ -367,17 +367,20 @@ Note that all comparison operators, such as `==`, `>`, `<`, should not be split. if (condition) list.map { foo(it) }.filter { bar(it) } else list.drop(1) ``` -**Note:** If dot qualified expression is inside condition or passed as an argument - replace with new variable +**Note:** If dot qualified expression is inside condition or passed as an argument - it should be replaced with new variable. **Invalid example**: ```kotlin - if (node.text.length.dec() != 0) {} + if (node.treeParent.treeParent.findChildByType(IDENTIFIER) != null) {} ``` **Valid example**: ```kotlin - val nodeLen = node.text.length.dec() - if (nodeLen != 0) {} + val grandIdentifier = node + .treeParent + .treeParent + .findChildByType(IDENTIFIER) + if (grandIdentifier != null) {} ``` 2) Newlines should be placed after the assignment operator (`=`). From 3d8af995e35d3ef29320f0c1b409ea24635eb999 Mon Sep 17 00:00:00 2001 From: kentr0w Date: Mon, 11 Jan 2021 18:22:06 +0300 Subject: [PATCH 9/9] New rule for complex expression ### What's done: Fixed after review --- .../diktat/ruleset/rules/files/NewlinesRule.kt | 6 +++--- .../chapter3/files/NewlinesRuleWarnTest.kt | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt index 988bcc03fe..f32760c2ae 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/NewlinesRule.kt @@ -447,9 +447,9 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" * * @return true - if there is error, and false if there is no error */ - private fun ASTNode.isCallsChain(dropLeadingBrackets: Boolean = true) = getCallChain(dropLeadingBrackets)?.isNotValidCalls(this) ?: false + private fun ASTNode.isCallsChain(dropLeadingProperties: Boolean = true) = getCallChain(dropLeadingProperties)?.isNotValidCalls(this) ?: false - private fun ASTNode.getCallChain(dropLeadingBrackets: Boolean = true): List? { + private fun ASTNode.getCallChain(dropLeadingProperties: Boolean = true): List? { val parentExpressionList = getParentExpressions() .lastOrNull() ?.run { @@ -457,7 +457,7 @@ class NewlinesRule(private val configRules: List) : Rule("newlines" getOrderedCallExpressions(psi, it) } } - return if (dropLeadingBrackets) { + return if (dropLeadingProperties) { // fixme: we can't distinguish fully qualified names (like java.lang) from chain of property accesses (like list.size) for now parentExpressionList?.dropWhile { !it.treeParent.textContains('(') && !it.treeParent.textContains('{') } } else { diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt index 32bb645457..3d95d8603d 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/NewlinesRuleWarnTest.kt @@ -907,6 +907,22 @@ class NewlinesRuleWarnTest : LintTestBase(::NewlinesRule) { ) } + @Test + @Tag(WarningNames.WRONG_NEWLINES) + fun `test for not first prefix`() { + lintMethod( + """ + |fun foo() { + | a().b!!.c() + | a().b.c()!! + |} + """.trimMargin(), + LintError(2, 11, ruleId, "$functionalStyleWarn .", true), + LintError(3, 9, ruleId, "$functionalStyleWarn .", true), + rulesConfigList = rulesConfigListShort + ) + } + @Test @Tag(WarningNames.WRONG_NEWLINES) fun `test for null safety several lines`() {