All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.gitlab.arturbosch.detekt.rules.style.ExplicitCollectionElementAccessMethod.kt Maven / Gradle / Ivy

package io.gitlab.arturbosch.detekt.rules.style

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 io.gitlab.arturbosch.detekt.rules.fqNameOrNull
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.supertypes

/**
 * In Kotlin functions `get` or `set` can be replaced with the shorter operator — `[]`,
 * see https://kotlinlang.org/docs/reference/operator-overloading.html#indexed.
 * Prefer the usage of the indexed access operator `[]` for map or list element access or insert methods.
 *
 * 
 *  val map = Map()
 *  map.put("key", "value")
 *  val value = map.get("key")
 * 
 *
 * 
 *  val map = Map()
 *  map["key"] = "value"
 *  val value = map["key"]
 * 
 */
class ExplicitCollectionElementAccessMethod(config: Config = Config.empty) : Rule(config) {

    private val ktCollections = setOf(
        "kotlin.collections.Map",
        "kotlin.collections.MutableMap",
        "kotlin.collections.List",
        "kotlin.collections.MutableList"
    )

    private val mapAccessMethods = setOf("get", "put")

    private val ktAndJavaCollections = ktCollections + setOf("AbstractMap", "AbstractList")

    override val issue: Issue =
        Issue(
            "ExplicitCollectionElementAccessMethod",
            Severity.Style,
            "Prefer usage of indexed access operator [] for map element access or insert methods",
            Debt.FIVE_MINS
        )

    override fun visitCallExpression(expression: KtCallExpression) {
        if (isMapMethod(expression) && isGetOrPut(expression)) {
            report(CodeSmell(issue, Entity.from(expression), "Prefer usage of indexed access operator []."))
        }
        super.visitCallExpression(expression)
    }

    private fun isGetOrPut(expression: KtCallExpression): Boolean {
        return expression.calleeExpression?.text in mapAccessMethods
    }

    private fun isMapMethod(expression: KtCallExpression): Boolean {
        val dotExpression = expression.prevSibling
        val caller = when (dotExpression?.parent) {
            is KtDotQualifiedExpression -> dotExpression.prevSibling
            else -> return false
        }
        return (caller as? KtElement).getResolvedCall(bindingContext)
            ?.resultingDescriptor
            ?.returnType
            .isEligibleCollection()
    }

    private fun KotlinType?.isEligibleCollection(): Boolean {
        if (this?.fqNameOrNull()?.asString() in ktCollections) return true
        return this?.supertypes()?.any { it.constructor.toString() in ktAndJavaCollections } == true
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy