From 02b39609491858fba8963859744f7c34e5b0b333 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Wed, 14 Sep 2022 15:50:11 +0300 Subject: [PATCH 1/7] Fix simplification of boolean expression ### What's done: * added test It closes #1521 --- .../ruleset/chapter3/BooleanExpressionsRuleFixTest.kt | 6 ++++++ .../ExpressionSimplificationExpected.kt | 8 ++++++++ .../boolean_expressions/ExpressionSimplificationTest.kt | 9 +++++++++ 3 files changed, 23 insertions(+) create mode 100644 diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt create mode 100644 diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationTest.kt diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/BooleanExpressionsRuleFixTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/BooleanExpressionsRuleFixTest.kt index f5f4d0722b..fbd015f7a5 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/BooleanExpressionsRuleFixTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/BooleanExpressionsRuleFixTest.kt @@ -43,4 +43,10 @@ class BooleanExpressionsRuleFixTest : FixTestBase("test/paragraph3/boolean_expre fun `check handling of negative expression`() { fixAndCompare("NegativeExpressionExpected.kt", "NegativeExpressionTest.kt") } + + @Test + @Tag(WarningNames.COMPLEX_BOOLEAN_EXPRESSION) + fun `check expression simplification`() { + fixAndCompare("ExpressionSimplificationExpected.kt", "ExpressionSimplificationTest.kt") + } } diff --git a/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt new file mode 100644 index 0000000000..9c929364fa --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt @@ -0,0 +1,8 @@ +package test.paragraph3.boolean_expressions + +fun F.foo() { + if (this.valueParameters[i].getFunctionName() != other.valueParameters[i].getFunctionName() || this.valueParameters[i].getFunctionType() != other.valueParameters[i].getFunctionType() + ) { + return false + } +} diff --git a/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationTest.kt b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationTest.kt new file mode 100644 index 0000000000..1734489e15 --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationTest.kt @@ -0,0 +1,9 @@ +package test.paragraph3.boolean_expressions + +fun F.foo() { + if (!(this.valueParameters[i].getFunctionName() == other.valueParameters[i].getFunctionName() && + this.valueParameters[i].getFunctionType() == other.valueParameters[i].getFunctionType()) + ) { + return false + } +} From 0263ea131d21c7807d13f924e74eb69bc178f0b7 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Wed, 14 Sep 2022 18:07:01 +0300 Subject: [PATCH 2/7] Used BINARY_EXPRESSION --- .../rules/chapter3/BooleanExpressionsRule.kt | 27 +++++++++++++------ .../ExpressionSimplificationExpected.kt | 11 ++++++-- .../ExpressionSimplificationTest.kt | 12 +++++++-- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt index 10b4e0394e..65c9aec10f 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt @@ -176,13 +176,13 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( * Note: mapping is String to Char(and Char to Char) actually, but will keep it as String for simplicity */ internal inner class ExpressionsReplacement { - private val expressionToToken: HashMap = LinkedHashMap() + private val expressionToToken: HashMap> = LinkedHashMap() private val tokenToOrderedToken: HashMap = HashMap() /** * TokenMapper for first call ExprParser which remembers the order of expression. */ - val orderedTokenMapper: TokenMapper = TokenMapper { name -> getLetter(tokenToOrderedToken, name) } + val orderedTokenMapper: TokenMapper = TokenMapper { name -> getLetter(tokenToOrderedToken, name) { it } } /** * Returns true if this object contains no replacements. @@ -207,7 +207,13 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( val expressionText = expressionAstNode.textWithoutComments() // support case when `boolean_expression` matches to `!boolean_expression` val expression = if (expressionText.startsWith('!')) expressionText.substring(1) else expressionText - getLetter(expressionToToken, expression) + getLetter(expressionToToken, expression) { letter -> + letter to if (expressionAstNode.elementType == BINARY_EXPRESSION) { + "($expression)" + } else { + expression + } + } } /** @@ -222,7 +228,7 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( expressionToToken.keys .sortedByDescending { it.length } .forEach { refExpr -> - resultExpression = resultExpression.replace(refExpr, expressionToToken[refExpr]!!) + resultExpression = resultExpression.replace(refExpr, expressionToToken.getValue(refExpr).first) } return resultExpression } @@ -241,16 +247,21 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( } resultExpression = resultExpression.format(args = tokenToOrderedToken.keys.toTypedArray()) // restore expression - expressionToToken.values.forEachIndexed { index, value -> + expressionToToken.values.forEachIndexed { index, (value, _) -> resultExpression = resultExpression.replace(value, "%${index + 1}\$s") } - resultExpression = resultExpression.format(args = expressionToToken.keys.toTypedArray()) + resultExpression = resultExpression.format(args = expressionToToken.values.map { it.second }.toTypedArray()) return resultExpression } - private fun getLetter(letters: HashMap, key: String) = letters + private fun getLetter( + letters: HashMap, + key: String, + valueBuilder: (String) -> T + ) = letters .computeIfAbsent(key) { - ('A'.code + letters.size).toChar().toString() + val letter = ('A'.code + letters.size).toChar().toString() + valueBuilder(letter) } } diff --git a/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt index 9c929364fa..5b6ca7fc02 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt @@ -1,7 +1,14 @@ package test.paragraph3.boolean_expressions -fun F.foo() { - if (this.valueParameters[i].getFunctionName() != other.valueParameters[i].getFunctionName() || this.valueParameters[i].getFunctionType() != other.valueParameters[i].getFunctionType() +fun F.foo1() { + if (!(this.valueParameters[i].getFunctionName() == other.valueParameters[i].getFunctionName()) || !(this.valueParameters[i].getFunctionType() != other.valueParameters[i].getFunctionType()) + ) { + return false + } +} + +fun F.foo2() { + if (!(this.valueParameters[i].getFunctionName() > other.valueParameters[i].getFunctionName()) || !(this.valueParameters[i].getFunctionType() < other.valueParameters[i].getFunctionType()) ) { return false } diff --git a/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationTest.kt b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationTest.kt index 1734489e15..5daead3134 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationTest.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationTest.kt @@ -1,8 +1,16 @@ package test.paragraph3.boolean_expressions -fun F.foo() { +fun F.foo1() { if (!(this.valueParameters[i].getFunctionName() == other.valueParameters[i].getFunctionName() && - this.valueParameters[i].getFunctionType() == other.valueParameters[i].getFunctionType()) + this.valueParameters[i].getFunctionType() != other.valueParameters[i].getFunctionType()) + ) { + return false + } +} + +fun F.foo2() { + if (!(this.valueParameters[i].getFunctionName() > other.valueParameters[i].getFunctionName() && + this.valueParameters[i].getFunctionType() < other.valueParameters[i].getFunctionType()) ) { return false } From be9a203cef6d8a68f89b30d9494e80b2ad17f3ff Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Wed, 14 Sep 2022 18:37:59 +0300 Subject: [PATCH 3/7] diktatFix --- .../diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt index 65c9aec10f..6dec22314d 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt @@ -27,6 +27,8 @@ import org.jetbrains.kotlin.psi.KtParenthesizedExpression import org.jetbrains.kotlin.psi.KtPrefixExpression import org.jetbrains.kotlin.psi.psiUtil.parents +typealias ExpressionMapping = HashMap> + /** * Rule that checks if the boolean expression can be simplified. */ From dd40f04e57774055397865e9aeb24b9e152bf4ec Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 15 Sep 2022 12:15:11 +0300 Subject: [PATCH 4/7] Supported negative replacements --- .../rules/chapter3/BooleanExpressionsRule.kt | 57 +++++++++++-------- .../cqfn/diktat/ruleset/utils/AstConstants.kt | 15 ++++- .../ExpressionSimplificationExpected.kt | 4 +- 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt index 6dec22314d..b095a60a12 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt @@ -5,6 +5,7 @@ import org.cqfn.diktat.ruleset.constants.Warnings.COMPLEX_BOOLEAN_EXPRESSION import org.cqfn.diktat.ruleset.rules.DiktatRule import org.cqfn.diktat.ruleset.utils.KotlinParser import org.cqfn.diktat.ruleset.utils.findAllNodesWithCondition +import org.cqfn.diktat.ruleset.utils.logicalInfixMethodMapping import org.cqfn.diktat.ruleset.utils.logicalInfixMethods import com.bpodgursky.jbool_expressions.Expression import com.bpodgursky.jbool_expressions.options.ExprOptions @@ -15,6 +16,7 @@ import com.bpodgursky.jbool_expressions.rules.DistributiveLaw import com.bpodgursky.jbool_expressions.rules.Rule import com.bpodgursky.jbool_expressions.rules.RuleList import com.bpodgursky.jbool_expressions.rules.RulesHelper +import com.pinterest.ktlint.core.ast.ElementType import com.pinterest.ktlint.core.ast.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.CONDITION import com.pinterest.ktlint.core.ast.ElementType.PARENTHESIZED @@ -22,13 +24,12 @@ import com.pinterest.ktlint.core.ast.ElementType.PREFIX_EXPRESSION import com.pinterest.ktlint.core.ast.isLeaf import com.pinterest.ktlint.core.ast.isPartOfComment import org.jetbrains.kotlin.com.intellij.lang.ASTNode +import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtParenthesizedExpression import org.jetbrains.kotlin.psi.KtPrefixExpression import org.jetbrains.kotlin.psi.psiUtil.parents -typealias ExpressionMapping = HashMap> - /** * Rule that checks if the boolean expression can be simplified. */ @@ -90,8 +91,7 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( internal fun formatBooleanExpressionAsString(node: ASTNode, expressionsReplacement: ExpressionsReplacement): String { val (booleanBinaryExpressions, otherBinaryExpressions) = node.collectElementaryExpressions() val logicalExpressions = otherBinaryExpressions.filter { otherBinaryExpression -> - // keeping only boolean expressions, keeping things like `a + b < 6` and excluding `a + b` - (otherBinaryExpression.psi as KtBinaryExpression).operationReference.text in logicalInfixMethods && + otherBinaryExpression.isLogicalOperation() && // todo: support xor; for now skip all expressions that are nested in xor otherBinaryExpression.parents() .takeWhile { it != node } @@ -143,6 +143,10 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( operationReferenceText == "&&" || operationReferenceText == "||" } + // keeping only boolean expressions, keeping things like `a + b < 6` and excluding `a + b` + private fun ASTNode.isLogicalOperation(): Boolean = + (psi as KtBinaryExpression).operationReference.text in logicalInfixMethods + private fun ASTNode.removeAllParentheses(): ASTNode { val result = (this.psi as? KtParenthesizedExpression)?.expression?.node ?: return this return result.removeAllParentheses() @@ -178,13 +182,14 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( * Note: mapping is String to Char(and Char to Char) actually, but will keep it as String for simplicity */ internal inner class ExpressionsReplacement { - private val expressionToToken: HashMap> = LinkedHashMap() + private val expressionToToken: HashMap = LinkedHashMap() + private val tokenToExpression: HashMap = HashMap() private val tokenToOrderedToken: HashMap = HashMap() /** * TokenMapper for first call ExprParser which remembers the order of expression. */ - val orderedTokenMapper: TokenMapper = TokenMapper { name -> getLetter(tokenToOrderedToken, name) { it } } + val orderedTokenMapper: TokenMapper = TokenMapper { name -> getLetter(tokenToOrderedToken, name) } /** * Returns true if this object contains no replacements. @@ -208,14 +213,14 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( fun addExpression(expressionAstNode: ASTNode) { val expressionText = expressionAstNode.textWithoutComments() // support case when `boolean_expression` matches to `!boolean_expression` - val expression = if (expressionText.startsWith('!')) expressionText.substring(1) else expressionText - getLetter(expressionToToken, expression) { letter -> - letter to if (expressionAstNode.elementType == BINARY_EXPRESSION) { - "($expression)" - } else { - expression - } + val (expression, negativeExpression) = if (expressionText.startsWith('!')) { + expressionText.substring(1) to expressionText + } else { + expressionText to getNegativeExpression(expressionAstNode, expressionText) } + val letter = getLetter(expressionToToken, expression) + tokenToExpression["!$letter"] = negativeExpression + tokenToExpression[letter] = expression } /** @@ -230,7 +235,7 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( expressionToToken.keys .sortedByDescending { it.length } .forEach { refExpr -> - resultExpression = resultExpression.replace(refExpr, expressionToToken.getValue(refExpr).first) + resultExpression = resultExpression.replace(refExpr, expressionToToken.getValue(refExpr)) } return resultExpression } @@ -249,22 +254,28 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( } resultExpression = resultExpression.format(args = tokenToOrderedToken.keys.toTypedArray()) // restore expression - expressionToToken.values.forEachIndexed { index, (value, _) -> + tokenToExpression.keys.forEachIndexed { index, value -> resultExpression = resultExpression.replace(value, "%${index + 1}\$s") } - resultExpression = resultExpression.format(args = expressionToToken.values.map { it.second }.toTypedArray()) + resultExpression = resultExpression.format(args = tokenToExpression.values.toTypedArray()) return resultExpression } - private fun getLetter( - letters: HashMap, - key: String, - valueBuilder: (String) -> T - ) = letters + private fun getLetter(letters: HashMap, key: String) = letters .computeIfAbsent(key) { - val letter = ('A'.code + letters.size).toChar().toString() - valueBuilder(letter) + ('A'.code + letters.size).toChar().toString() } + + private fun getNegativeExpression(expressionAstNode: ASTNode, expression: String): String { + return if (expressionAstNode.elementType == BINARY_EXPRESSION) { + val operation = (expressionAstNode.psi as KtBinaryExpression).operationReference.text + logicalInfixMethodMapping[operation]?.let { + expression.replace(operation, it) + } ?: "!($expression)" + } else { + "!$expression" + } + } } companion object { diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstConstants.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstConstants.kt index f6637ac519..bdd4a984d3 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstConstants.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstConstants.kt @@ -22,10 +22,23 @@ internal const val EMPTY_BLOCK_TEXT = "{}" */ internal val standardMethods = listOf("main", "equals", "hashCode", "toString", "clone", "finalize") +/** + * Mapping (value is negative infix) of infix methods that return Boolean + */ +internal val logicalInfixMethodMapping = mapOf( + "==" to "!=", + "!=" to "==", + ">" to "<=", + "<" to ">=", + ">=" to "<", + "<=" to ">", + "in" to "!in", + "!in" to "in", +) /** * List of infix methods that return Boolean */ -internal val logicalInfixMethods = setOf("==", "!=", ">", "<", ">=", "<=", "in", "!in", "xor") +internal val logicalInfixMethods = logicalInfixMethodMapping.keys + "xor" /** * List of element types present in empty code block `{ }` diff --git a/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt index 5b6ca7fc02..814f2fd023 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt @@ -1,14 +1,14 @@ package test.paragraph3.boolean_expressions fun F.foo1() { - if (!(this.valueParameters[i].getFunctionName() == other.valueParameters[i].getFunctionName()) || !(this.valueParameters[i].getFunctionType() != other.valueParameters[i].getFunctionType()) + if (this.valueParameters[i].getFunctionName() != other.valueParameters[i].getFunctionName() || this.valueParameters[i].getFunctionType() == other.valueParameters[i].getFunctionType() ) { return false } } fun F.foo2() { - if (!(this.valueParameters[i].getFunctionName() > other.valueParameters[i].getFunctionName()) || !(this.valueParameters[i].getFunctionType() < other.valueParameters[i].getFunctionType()) + if (this.valueParameters[i].getFunctionName() <= other.valueParameters[i].getFunctionName() || this.valueParameters[i].getFunctionType() >= other.valueParameters[i].getFunctionType() ) { return false } From b13a18140c6b9a23fe460545a7cff3e49e5dc45b Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 15 Sep 2022 12:18:43 +0300 Subject: [PATCH 5/7] diktatFix --- .../rules/chapter3/BooleanExpressionsRule.kt | 28 ++++++++----------- .../cqfn/diktat/ruleset/utils/AstConstants.kt | 1 + 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt index b095a60a12..d95abea344 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt @@ -16,7 +16,6 @@ import com.bpodgursky.jbool_expressions.rules.DistributiveLaw import com.bpodgursky.jbool_expressions.rules.Rule import com.bpodgursky.jbool_expressions.rules.RuleList import com.bpodgursky.jbool_expressions.rules.RulesHelper -import com.pinterest.ktlint.core.ast.ElementType import com.pinterest.ktlint.core.ast.ElementType.BINARY_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.CONDITION import com.pinterest.ktlint.core.ast.ElementType.PARENTHESIZED @@ -24,7 +23,6 @@ import com.pinterest.ktlint.core.ast.ElementType.PREFIX_EXPRESSION import com.pinterest.ktlint.core.ast.isLeaf import com.pinterest.ktlint.core.ast.isPartOfComment import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtParenthesizedExpression import org.jetbrains.kotlin.psi.KtPrefixExpression @@ -91,7 +89,8 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( internal fun formatBooleanExpressionAsString(node: ASTNode, expressionsReplacement: ExpressionsReplacement): String { val (booleanBinaryExpressions, otherBinaryExpressions) = node.collectElementaryExpressions() val logicalExpressions = otherBinaryExpressions.filter { otherBinaryExpression -> - otherBinaryExpression.isLogicalOperation() && + // keeping only boolean expressions, keeping things like `a + b < 6` and excluding `a + b` + (otherBinaryExpression.psi as KtBinaryExpression).operationReference.text in logicalInfixMethods && // todo: support xor; for now skip all expressions that are nested in xor otherBinaryExpression.parents() .takeWhile { it != node } @@ -143,10 +142,6 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( operationReferenceText == "&&" || operationReferenceText == "||" } - // keeping only boolean expressions, keeping things like `a + b < 6` and excluding `a + b` - private fun ASTNode.isLogicalOperation(): Boolean = - (psi as KtBinaryExpression).operationReference.text in logicalInfixMethods - private fun ASTNode.removeAllParentheses(): ASTNode { val result = (this.psi as? KtParenthesizedExpression)?.expression?.node ?: return this return result.removeAllParentheses() @@ -266,16 +261,15 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( ('A'.code + letters.size).toChar().toString() } - private fun getNegativeExpression(expressionAstNode: ASTNode, expression: String): String { - return if (expressionAstNode.elementType == BINARY_EXPRESSION) { - val operation = (expressionAstNode.psi as KtBinaryExpression).operationReference.text - logicalInfixMethodMapping[operation]?.let { - expression.replace(operation, it) - } ?: "!($expression)" - } else { - "!$expression" - } - } + private fun getNegativeExpression(expressionAstNode: ASTNode, expression: String): String = + if (expressionAstNode.elementType == BINARY_EXPRESSION) { + val operation = (expressionAstNode.psi as KtBinaryExpression).operationReference.text + logicalInfixMethodMapping[operation]?.let { + expression.replace(operation, it) + } ?: "!($expression)" + } else { + "!$expression" + } } companion object { diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstConstants.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstConstants.kt index bdd4a984d3..b7eb229084 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstConstants.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/AstConstants.kt @@ -35,6 +35,7 @@ internal val logicalInfixMethodMapping = mapOf( "in" to "!in", "!in" to "in", ) + /** * List of infix methods that return Boolean */ From 9612e23abfd35f5ad495e27ee984beaee01bf456 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 15 Sep 2022 12:23:16 +0300 Subject: [PATCH 6/7] diktatFix #2 --- .../rules/chapter3/BooleanExpressionsRule.kt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt index d95abea344..a793605c87 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/BooleanExpressionsRule.kt @@ -224,7 +224,6 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( * @param fullExpression full boolean expression in kotlin * @return full expression in jbool format */ - @Suppress("UnsafeCallOnNullableType") fun replaceExpressions(fullExpression: String): String { var resultExpression = fullExpression expressionToToken.keys @@ -262,14 +261,14 @@ class BooleanExpressionsRule(configRules: List) : DiktatRule( } private fun getNegativeExpression(expressionAstNode: ASTNode, expression: String): String = - if (expressionAstNode.elementType == BINARY_EXPRESSION) { - val operation = (expressionAstNode.psi as KtBinaryExpression).operationReference.text - logicalInfixMethodMapping[operation]?.let { - expression.replace(operation, it) - } ?: "!($expression)" - } else { - "!$expression" - } + if (expressionAstNode.elementType == BINARY_EXPRESSION) { + val operation = (expressionAstNode.psi as KtBinaryExpression).operationReference.text + logicalInfixMethodMapping[operation]?.let { + expression.replace(operation, it) + } ?: "!($expression)" + } else { + "!$expression" + } } companion object { From 1817be4b713648a73bf39aad2f1f4db07bd3b8a6 Mon Sep 17 00:00:00 2001 From: Nariman Abdullin Date: Thu, 15 Sep 2022 12:38:01 +0300 Subject: [PATCH 7/7] added extra test for xor --- .../ExpressionSimplificationExpected.kt | 7 +++++++ .../boolean_expressions/ExpressionSimplificationTest.kt | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt index 814f2fd023..5530dfea07 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationExpected.kt @@ -13,3 +13,10 @@ fun F.foo2() { return false } } + +fun F.foo3() { + if (!(this.valueParameters[i].getFunctionName() xor other.valueParameters[i].getFunctionName()) || !(this.valueParameters[i].getFunctionType() xor other.valueParameters[i].getFunctionType()) + ) { + return false + } +} diff --git a/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationTest.kt b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationTest.kt index 5daead3134..2e6c225278 100644 --- a/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationTest.kt +++ b/diktat-rules/src/test/resources/test/paragraph3/boolean_expressions/ExpressionSimplificationTest.kt @@ -15,3 +15,11 @@ fun F.foo2() { return false } } + +fun F.foo3() { + if (!(this.valueParameters[i].getFunctionName() xor other.valueParameters[i].getFunctionName() && + this.valueParameters[i].getFunctionType() xor other.valueParameters[i].getFunctionType()) + ) { + return false + } +}