<commit_before>package io.gitlab.arturbosch.detekt.rules.performance

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.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull

/**
 * Using Array<Primitive> leads to implicit boxing and performance hit. Prefer using Kotlin specialized Array
 * Instances.
 *
 * As stated in the Kotlin [documentation](https://kotlinlang.org/docs/reference/basic-types.html#arrays) Kotlin has
 * specialized arrays to represent primitive types without boxing overhead, such as `IntArray`, `ByteArray` and so on.
 *
 * <noncompliant>
 * fun function(array: Array<Int>) { }
 *
 * fun returningFunction(): Array<Double> { }
 * </noncompliant>
 *
 * <compliant>
 * fun function(array: IntArray) { }
 *
 * fun returningFunction(): DoubleArray { }
 * </compliant>
 *
 * @active since v1.2.0
 * @requiresTypeResolution
 */
class ArrayPrimitive(config: Config = Config.empty) : Rule(config) {
    override val issue = Issue(
        "ArrayPrimitive",
        Severity.Performance,
        "Using Array<Primitive> leads to implicit boxing and a performance hit",
        Debt.FIVE_MINS
    )

    @Suppress("ReturnCount")
    override fun visitCallExpression(expression: KtCallExpression) {
        super.visitCallExpression(expression)
        if (bindingContext == BindingContext.EMPTY) return

        if (expression.calleeExpression?.text !in factoryMethodNames) return
        val descriptor = expression.getResolvedCall(bindingContext)?.resultingDescriptor ?: return
        if (descriptor.fqNameOrNull() !in factoryMethodFqNames) return

        val type = descriptor.returnType?.arguments?.singleOrNull()?.type ?: return
        if (KotlinBuiltIns.isPrimitiveType(type)) {
            report(CodeSmell(issue, Entity.from(expression), issue.description))
        }
    }

    override fun visitNamedDeclaration(declaration: KtNamedDeclaration) {
        super.visitNamedDeclaration(declaration)
        if (declaration is KtCallableDeclaration) {
            reportArrayPrimitives(declaration.typeReference)
            reportArrayPrimitives(declaration.receiverTypeReference)
        }
    }

    private fun reportArrayPrimitives(typeReference: KtTypeReference?) {
        typeReference
            ?.collectDescendantsOfType<KtTypeReference> { isArrayPrimitive(it) }
            ?.forEach { report(CodeSmell(issue, Entity.from(it), issue.description)) }
    }

    private fun isArrayPrimitive(it: KtTypeReference): Boolean {
        if (it.text?.startsWith("Array<") == true) {
            val genericTypeArguments = it.typeElement?.typeArgumentsAsTypes
            return genericTypeArguments?.singleOrNull()?.let { primitiveTypes.contains(it.text) } == true
        }
        return false
    }

    companion object {
        private val primitiveTypes = PrimitiveType.values().map { it.typeName.asString() }
        private val factoryMethodFqNames = listOf(FqName("kotlin.arrayOf"), FqName("kotlin.emptyArray"))
        private val factoryMethodNames = factoryMethodFqNames.map { it.shortName().asString() }
    }
}<commit_msg>Remove Suppress annotation from ArrayPrimitive (#3209)<commit_after>package io.gitlab.arturbosch.detekt.rules.performance

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.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull

/**
 * Using Array<Primitive> leads to implicit boxing and performance hit. Prefer using Kotlin specialized Array
 * Instances.
 *
 * As stated in the Kotlin [documentation](https://kotlinlang.org/docs/reference/basic-types.html#arrays) Kotlin has
 * specialized arrays to represent primitive types without boxing overhead, such as `IntArray`, `ByteArray` and so on.
 *
 * <noncompliant>
 * fun function(array: Array<Int>) { }
 *
 * fun returningFunction(): Array<Double> { }
 * </noncompliant>
 *
 * <compliant>
 * fun function(array: IntArray) { }
 *
 * fun returningFunction(): DoubleArray { }
 * </compliant>
 *
 * @active since v1.2.0
 * @requiresTypeResolution
 */
class ArrayPrimitive(config: Config = Config.empty) : Rule(config) {
    override val issue = Issue(
        "ArrayPrimitive",
        Severity.Performance,
        "Using Array<Primitive> leads to implicit boxing and a performance hit",
        Debt.FIVE_MINS
    )

    override fun visitCallExpression(expression: KtCallExpression) {
        super.visitCallExpression(expression)
        if (bindingContext == BindingContext.EMPTY) return
        if (expression.calleeExpression?.text !in factoryMethodNames) return

        val descriptor = expression.getResolvedCall(bindingContext)?.resultingDescriptor
        if (descriptor != null && isArrayPrimitive(descriptor)) {
            report(CodeSmell(issue, Entity.from(expression), issue.description))
        }
    }

    override fun visitNamedDeclaration(declaration: KtNamedDeclaration) {
        super.visitNamedDeclaration(declaration)
        if (declaration is KtCallableDeclaration) {
            reportArrayPrimitives(declaration.typeReference)
            reportArrayPrimitives(declaration.receiverTypeReference)
        }
    }

    private fun reportArrayPrimitives(typeReference: KtTypeReference?) {
        typeReference
            ?.collectDescendantsOfType<KtTypeReference> { isArrayPrimitive(it) }
            ?.forEach { report(CodeSmell(issue, Entity.from(it), issue.description)) }
    }

    private fun isArrayPrimitive(descriptor: CallableDescriptor): Boolean {
        val type = descriptor.returnType?.arguments?.singleOrNull()?.type
        return descriptor.fqNameOrNull() in factoryMethodFqNames && type != null && KotlinBuiltIns.isPrimitiveType(type)
    }

    private fun isArrayPrimitive(it: KtTypeReference): Boolean {
        if (it.text?.startsWith("Array<") == true) {
            val genericTypeArguments = it.typeElement?.typeArgumentsAsTypes
            return genericTypeArguments?.singleOrNull()?.let { primitiveTypes.contains(it.text) } == true
        }
        return false
    }

    companion object {
        private val primitiveTypes = PrimitiveType.values().map { it.typeName.asString() }
        private val factoryMethodFqNames = listOf(FqName("kotlin.arrayOf"), FqName("kotlin.emptyArray"))
        private val factoryMethodNames = factoryMethodFqNames.map { it.shortName().asString() }
    }
}
{'fraction_non_alphanumeric': 0.1000629326620516, 'fraction_numerical': 0.0012586532410320957, 'mean_word_length': 4.08381317978247, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 21, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '425323', 'n_tokens_mistral': 2372, 'n_tokens_neox': 2302, 'n_words': 429}