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..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 @@ -18,13 +18,16 @@ 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 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 /** * This rule checks if class can be made as data class @@ -59,8 +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 hasPropertyInConstructor = findChildByType(PRIMARY_CONSTRUCTOR) + ?.let { constructor -> + (constructor.psi as KtPrimaryConstructor) + .valueParameters + .run { isNotEmpty() && all { it.hasValOrVar() } } + } ?: false + if (isNotPropertyInClassBody && !hasPropertyInConstructor) { + 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 64d76db66c..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 @@ -96,4 +96,56 @@ 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 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(1, 1, ruleId, "${Warnings.USE_DATA_CLASS.warnText()} B"), + LintError(5, 1, ruleId, "${Warnings.USE_DATA_CLASS.warnText()} Ab") + ) + } }