Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataClassesRule shouldn't trigger when no property in constructor #671

Merged
merged 8 commits into from
Dec 29, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -59,8 +62,18 @@ class DataClassesRule(private val configRule: List<RulesConfig>) : 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)!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Int,Int>) {}
|
|class Ab(val map: Map<Int, Int>, map: Map<Int, Int>) {}
|
|class A(val map: Map<Int, Int>) {}
|
""".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")
)
}
}