From 244a54993fe4a01030749ebed45de7714dcb9c12 Mon Sep 17 00:00:00 2001 From: kentr0w Date: Mon, 28 Dec 2020 15:56:56 +0300 Subject: [PATCH 1/7] DataClassesRule shouldn't trigger when no property in constructor ### What's done: Fixed bug, added test --- .../ruleset/rules/classes/DataClassesRule.kt | 18 +++++++++++------- .../chapter6/DataClassesRuleWarnTest.kt | 13 +++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt index 8bc64a4bf1..9598cc2371 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt @@ -18,6 +18,7 @@ import com.pinterest.ktlint.core.ast.ElementType.FUN import com.pinterest.ktlint.core.ast.ElementType.INNER_KEYWORD import com.pinterest.ktlint.core.ast.ElementType.MODIFIER_LIST import com.pinterest.ktlint.core.ast.ElementType.OPEN_KEYWORD +import com.pinterest.ktlint.core.ast.ElementType.PRIMARY_CONSTRUCTOR import com.pinterest.ktlint.core.ast.ElementType.PROPERTY import com.pinterest.ktlint.core.ast.ElementType.PROPERTY_ACCESSOR import com.pinterest.ktlint.core.ast.ElementType.SEALED_KEYWORD @@ -25,6 +26,7 @@ import com.pinterest.ktlint.core.ast.ElementType.SUPER_TYPE_LIST import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtPrimaryConstructor /** * This rule checks if class can be made as data class @@ -61,21 +63,23 @@ class DataClassesRule(private val configRule: List) : Rule("data-cl @Suppress("UnsafeCallOnNullableType", "FUNCTION_BOOLEAN_PREFIX") private fun ASTNode.canBeDataClass(): Boolean { + if (findChildByType(PRIMARY_CONSTRUCTOR)?.let { constructor -> (constructor.psi as KtPrimaryConstructor).valueParameters.none { it.hasValOrVar() } } == true) + return false val classBody = getFirstChildWithType(CLASS_BODY) if (hasChildOfType(MODIFIER_LIST)) { val list = getFirstChildWithType(MODIFIER_LIST)!! return list.getChildren(null) - .none { it.elementType in badModifiers } && + .none { it.elementType in badModifiers } && classBody?.getAllChildrenWithType(FUN) - ?.isEmpty() - ?: false && + ?.isEmpty() + ?: false && getFirstChildWithType(SUPER_TYPE_LIST) == null } return classBody?.getAllChildrenWithType(FUN)?.isEmpty() ?: false && getFirstChildWithType(SUPER_TYPE_LIST) == null && // if there is any prop with logic in accessor then don't recommend to convert class to data class classBody?.let(::areGoodProps) - ?: true + ?: true } /** @@ -103,9 +107,9 @@ class DataClassesRule(private val configRule: List) : Rule("data-cl val block = it.getFirstChildWithType(BLOCK)!! return block - .getChildren(null) - .filter { expr -> expr.psi is KtExpression } - .count() <= 1 + .getChildren(null) + .filter { expr -> expr.psi is KtExpression } + .count() <= 1 } } diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/DataClassesRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/DataClassesRuleWarnTest.kt index 64d76db66c..1cac6a5503 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/DataClassesRuleWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/DataClassesRuleWarnTest.kt @@ -96,4 +96,17 @@ class DataClassesRuleWarnTest : LintTestBase(::DataClassesRule) { """.trimMargin() ) } + + @Test + @Tag(USE_DATA_CLASS) + fun `should not trigger on classes with no property in constructor`() { + lintMethod( + """ + |class B(map: Map) {} + | + |class A(val map: Map) {} + """.trimMargin(), + LintError(3, 1, ruleId, "${Warnings.USE_DATA_CLASS.warnText()} A") + ) + } } From 1b87d8ddda3b647b8203877ce0ecfc068eadbc78 Mon Sep 17 00:00:00 2001 From: kentr0w Date: Mon, 28 Dec 2020 16:04:07 +0300 Subject: [PATCH 2/7] DataClassesRule shouldn't trigger when no property in constructor ### What's done: Fixed code-style --- .../ruleset/rules/classes/DataClassesRule.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt index 9598cc2371..4546a46aa4 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt @@ -63,23 +63,24 @@ class DataClassesRule(private val configRule: List) : Rule("data-cl @Suppress("UnsafeCallOnNullableType", "FUNCTION_BOOLEAN_PREFIX") private fun ASTNode.canBeDataClass(): Boolean { - if (findChildByType(PRIMARY_CONSTRUCTOR)?.let { constructor -> (constructor.psi as KtPrimaryConstructor).valueParameters.none { it.hasValOrVar() } } == true) + if (findChildByType(PRIMARY_CONSTRUCTOR)?.let { constructor -> (constructor.psi as KtPrimaryConstructor).valueParameters.none { it.hasValOrVar() } } == true) { return false + } val classBody = getFirstChildWithType(CLASS_BODY) if (hasChildOfType(MODIFIER_LIST)) { val list = getFirstChildWithType(MODIFIER_LIST)!! return list.getChildren(null) - .none { it.elementType in badModifiers } && + .none { it.elementType in badModifiers } && classBody?.getAllChildrenWithType(FUN) - ?.isEmpty() - ?: false && + ?.isEmpty() + ?: false && getFirstChildWithType(SUPER_TYPE_LIST) == null } return classBody?.getAllChildrenWithType(FUN)?.isEmpty() ?: false && getFirstChildWithType(SUPER_TYPE_LIST) == null && // if there is any prop with logic in accessor then don't recommend to convert class to data class classBody?.let(::areGoodProps) - ?: true + ?: true } /** @@ -107,9 +108,9 @@ class DataClassesRule(private val configRule: List) : Rule("data-cl val block = it.getFirstChildWithType(BLOCK)!! return block - .getChildren(null) - .filter { expr -> expr.psi is KtExpression } - .count() <= 1 + .getChildren(null) + .filter { expr -> expr.psi is KtExpression } + .count() <= 1 } } From 0c01abd72d0fffc890a69165095d25707c12e5a3 Mon Sep 17 00:00:00 2001 From: kentr0w Date: Mon, 28 Dec 2020 16:09:30 +0300 Subject: [PATCH 3/7] DataClassesRule shouldn't trigger when no property in constructor ### What's done: Fixed code-style --- .../org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt index 4546a46aa4..5b56f25c23 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt @@ -73,7 +73,7 @@ class DataClassesRule(private val configRule: List) : Rule("data-cl .none { it.elementType in badModifiers } && classBody?.getAllChildrenWithType(FUN) ?.isEmpty() - ?: false && + ?: false && getFirstChildWithType(SUPER_TYPE_LIST) == null } return classBody?.getAllChildrenWithType(FUN)?.isEmpty() ?: false && From 09ba528db50ed14efb7720f6d9349f036de32c35 Mon Sep 17 00:00:00 2001 From: kentr0w Date: Tue, 29 Dec 2020 11:31:09 +0300 Subject: [PATCH 4/7] DataClassesRule shouldn't trigger when no property in constructor ### What's done: Added logic for empty class and class with property --- .../ruleset/rules/classes/DataClassesRule.kt | 10 ++++- .../chapter6/DataClassesRuleWarnTest.kt | 41 ++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt index 5b56f25c23..eae668337f 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt @@ -25,6 +25,7 @@ import com.pinterest.ktlint.core.ast.ElementType.SEALED_KEYWORD import com.pinterest.ktlint.core.ast.ElementType.SUPER_TYPE_LIST import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtClassBody import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtPrimaryConstructor @@ -63,9 +64,14 @@ class DataClassesRule(private val configRule: List) : Rule("data-cl @Suppress("UnsafeCallOnNullableType", "FUNCTION_BOOLEAN_PREFIX") private fun ASTNode.canBeDataClass(): Boolean { - if (findChildByType(PRIMARY_CONSTRUCTOR)?.let { constructor -> (constructor.psi as KtPrimaryConstructor).valueParameters.none { it.hasValOrVar() } } == true) { + val isNotPropertyInClassBody = findChildByType(CLASS_BODY)?.let { (it.psi as KtClassBody).properties.isEmpty() } ?: true + val isNotPropertyInConstructor = findChildByType(PRIMARY_CONSTRUCTOR) + ?.let { constructor -> (constructor.psi as KtPrimaryConstructor) + .valueParameters + .run { isNotEmpty() && all { it.hasValOrVar() } } + } ?: false + if (isNotPropertyInClassBody && !isNotPropertyInConstructor) return false - } val classBody = getFirstChildWithType(CLASS_BODY) if (hasChildOfType(MODIFIER_LIST)) { val list = getFirstChildWithType(MODIFIER_LIST)!! diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/DataClassesRuleWarnTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/DataClassesRuleWarnTest.kt index 1cac6a5503..db405a5ea8 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/DataClassesRuleWarnTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter6/DataClassesRuleWarnTest.kt @@ -104,9 +104,48 @@ class DataClassesRuleWarnTest : LintTestBase(::DataClassesRule) { """ |class B(map: Map) {} | + |class Ab(val map: Map, map: Map) {} + | |class A(val map: Map) {} + | + """.trimMargin(), + LintError(5, 1, ruleId, "${Warnings.USE_DATA_CLASS.warnText()} A") + ) + } + + @Test + @Tag(USE_DATA_CLASS) + fun `should not trigger on empty class`() { + lintMethod( + """ + |class B() {} + | + |class Ab{} + | + """.trimMargin() + ) + } + + @Test + @Tag(USE_DATA_CLASS) + fun `should trigger on class without constructor but with property`() { + lintMethod( + """ + |class B() { + | val q = 10 + |} + | + |class Ab { + | val qw = 10 + |} + | + |class Ba { + | val q = 10 + | fun foo() = 10 + |} """.trimMargin(), - LintError(3, 1, ruleId, "${Warnings.USE_DATA_CLASS.warnText()} A") + LintError(1, 1, ruleId, "${Warnings.USE_DATA_CLASS.warnText()} B"), + LintError(5, 1, ruleId, "${Warnings.USE_DATA_CLASS.warnText()} Ab") ) } } From 7d49a3511b1c755a331feb398c611e4af3c9bf89 Mon Sep 17 00:00:00 2001 From: kentr0w Date: Tue, 29 Dec 2020 11:39:45 +0300 Subject: [PATCH 5/7] DataClassesRule shouldn't trigger when no property in constructor ### What's done: Fixed code-style --- .../diktat/ruleset/rules/classes/DataClassesRule.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt index eae668337f..e8d7f5807e 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt @@ -62,16 +62,18 @@ class DataClassesRule(private val configRule: List) : Rule("data-cl USE_DATA_CLASS.warn(configRule, emitWarn, isFixMode, "${(node.psi as KtClass).name}", node.startOffset, node) } - @Suppress("UnsafeCallOnNullableType", "FUNCTION_BOOLEAN_PREFIX") + @Suppress("UnsafeCallOnNullableType", "FUNCTION_BOOLEAN_PREFIX", "ComplexMethod") private fun ASTNode.canBeDataClass(): Boolean { val isNotPropertyInClassBody = findChildByType(CLASS_BODY)?.let { (it.psi as KtClassBody).properties.isEmpty() } ?: true val isNotPropertyInConstructor = findChildByType(PRIMARY_CONSTRUCTOR) - ?.let { constructor -> (constructor.psi as KtPrimaryConstructor) - .valueParameters - .run { isNotEmpty() && all { it.hasValOrVar() } } + ?.let { constructor -> + (constructor.psi as KtPrimaryConstructor) + .valueParameters + .run { isNotEmpty() && all { it.hasValOrVar() } } } ?: false - if (isNotPropertyInClassBody && !isNotPropertyInConstructor) + if (isNotPropertyInClassBody && !isNotPropertyInConstructor) { return false + } val classBody = getFirstChildWithType(CLASS_BODY) if (hasChildOfType(MODIFIER_LIST)) { val list = getFirstChildWithType(MODIFIER_LIST)!! From 5b77685504f4a8598da9450389c6f3f0fe18c5da Mon Sep 17 00:00:00 2001 From: kentr0w Date: Tue, 29 Dec 2020 12:28:13 +0300 Subject: [PATCH 6/7] DataClassesRule shouldn't trigger when no property in constructor ### What's done: Fixed code-style --- .../org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt index e8d7f5807e..67ff0c8d18 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt @@ -65,13 +65,13 @@ class DataClassesRule(private val configRule: List) : Rule("data-cl @Suppress("UnsafeCallOnNullableType", "FUNCTION_BOOLEAN_PREFIX", "ComplexMethod") private fun ASTNode.canBeDataClass(): Boolean { val isNotPropertyInClassBody = findChildByType(CLASS_BODY)?.let { (it.psi as KtClassBody).properties.isEmpty() } ?: true - val isNotPropertyInConstructor = findChildByType(PRIMARY_CONSTRUCTOR) + val hasPropertyInConstructor = findChildByType(PRIMARY_CONSTRUCTOR) ?.let { constructor -> (constructor.psi as KtPrimaryConstructor) .valueParameters .run { isNotEmpty() && all { it.hasValOrVar() } } } ?: false - if (isNotPropertyInClassBody && !isNotPropertyInConstructor) { + if (isNotPropertyInClassBody && !hasPropertyInConstructor) { return false } val classBody = getFirstChildWithType(CLASS_BODY) From 047009c3efd39607b788d767b8eb2c604a7356cf Mon Sep 17 00:00:00 2001 From: kentr0w Date: Tue, 29 Dec 2020 12:33:56 +0300 Subject: [PATCH 7/7] DataClassesRule shouldn't trigger when no property in constructor ### What's done: Fixed code-style --- .../org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt index 67ff0c8d18..1baefc23d9 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/classes/DataClassesRule.kt @@ -65,7 +65,7 @@ class DataClassesRule(private val configRule: List) : Rule("data-cl @Suppress("UnsafeCallOnNullableType", "FUNCTION_BOOLEAN_PREFIX", "ComplexMethod") private fun ASTNode.canBeDataClass(): Boolean { val isNotPropertyInClassBody = findChildByType(CLASS_BODY)?.let { (it.psi as KtClassBody).properties.isEmpty() } ?: true - val hasPropertyInConstructor = findChildByType(PRIMARY_CONSTRUCTOR) + val hasPropertyInConstructor = findChildByType(PRIMARY_CONSTRUCTOR) ?.let { constructor -> (constructor.psi as KtPrimaryConstructor) .valueParameters