要件
基本的に分解宣言(Destructuring) はクラス定義を辞めて Array<Object>
と引数 index でコミュニケーション取るようなもの(言い過ぎ)なので辞めたい。
例外
一方で以下のケースは許したい
言語 for 文での受け取り
多くは key/value や idx/value なので。。
for ((key, value) in entries) {
// OK
}
associatedBy 等の receiver
entries.associatedBy { (key, value) -> key }
実装
import io.gitlab.arturbosch.detekt.api.CodeSmell
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.Debt
import io.gitlab.arturbosch.detekt.api.Entity
import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
import org.jetbrains.kotlin.psi.KtForExpression
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.psiUtil.parents
class NoDestructuring(config: Config = Config.empty) : Rule(config) {
override val issue = Issue(
id = "NoDestructuring",
severity = Severity.Style,
description = "分解宣言は for-loop とラムダ(SAM)のパラメータだけ許可",
debt = Debt.FIVE_MINS,
)
override fun visitDestructuringDeclaration(d: KtDestructuringDeclaration) {
val allowed = isInForLoop(d) || isInLambdaParameter(d)
if (!allowed) {
report(
CodeSmell(
issue,
Entity.from(d),
"変数の定義順でのアクセスとなる分解宣言は利用せず、代わりに明示的なフィールドアクセスを行ってください。必要があれば (private) data class を定義してください。",
),
)
}
super.visitDestructuringDeclaration(d)
}
/** `for ((k,v) in ...) {}` の `(k,v)` 判定 */
private fun isInForLoop(d: KtDestructuringDeclaration): Boolean {
return d.parent is KtForExpression || d.parent?.parent is KtForExpression
}
/** `{ (k,v) -> ... }` ラムダの `(k,v)` 判定 */
private fun isInLambdaParameter(d: KtDestructuringDeclaration): Boolean {
return d.parents.any { it is KtParameter } &&
d.parents.any { it is KtLambdaExpression }
}
}