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

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

The newest version!
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.Location
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.ValueWithReason
import io.gitlab.arturbosch.detekt.api.config
import io.gitlab.arturbosch.detekt.api.internal.Configuration
import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution
import io.gitlab.arturbosch.detekt.api.valuesWithReason
import io.gitlab.arturbosch.detekt.rules.fqNameOrNull
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.KotlinType

/**
 * This rule allows to set a list of forbidden annotations. This can be used to discourage the use
 * of language annotations which do not require explicit import.
 *
 * 
 * @@SuppressWarnings("unused")
 * class SomeClass()
 * 
 *
 * 
 * @@Suppress("unused")
 * class SomeClass()
 * 
 */
@RequiresTypeResolution
class ForbiddenAnnotation(config: Config = Config.empty) : Rule(config) {

    override val issue = Issue(
        javaClass.simpleName,
        Severity.Style,
        "Avoid using this annotation.",
        Debt.FIVE_MINS
    )

    @Configuration(
        "List of fully qualified annotation classes which are forbidden."
    )
    private val annotations: Map by config(
        valuesWithReason(
            "java.lang.SuppressWarnings" to "it is a java annotation. Use `Suppress` instead.",
            "java.lang.Deprecated" to "it is a java annotation. Use `kotlin.Deprecated` instead.",
            "java.lang.annotation.Documented" to "it is a java annotation. " +
                "Use `kotlin.annotation.MustBeDocumented` instead.",
            "java.lang.annotation.Target" to "it is a java annotation. Use `kotlin.annotation.Target` instead.",
            "java.lang.annotation.Retention" to "it is a java annotation. Use `kotlin.annotation.Retention` instead.",
            "java.lang.annotation.Repeatable" to "it is a java annotation. Use `kotlin.annotation.Repeatable` instead.",
            "java.lang.annotation.Inherited" to "Kotlin does not support @Inherited annotation, " +
                "see https://youtrack.jetbrains.com/issue/KT-22265",
        )
    ) { list ->
        list.associateBy { it.value }
    }

    override fun visitAnnotationEntry(annotation: KtAnnotationEntry) {
        super.visitAnnotationEntry(annotation)

        annotation.typeReference?.fqNameOrNull()?.let {
            check(annotation, it)
        }
    }

    override fun visitExpression(expression: KtExpression) {
        super.visitExpression(expression)

        expression.expressionTypeOrNull()?.fqNameOrNull()?.let {
            check(expression, it)
        }
    }

    private fun check(element: KtElement, fqName: FqName) {
        val forbidden = annotations[fqName.asString()]

        if (forbidden != null) {
            val message = if (forbidden.reason != null) {
                "The annotation `${forbidden.value}` has been forbidden: ${forbidden.reason}"
            } else {
                "The annotation `${forbidden.value}` has been forbidden in the detekt config."
            }
            val location = Location.from(element).let { location ->
                location.copy(
                    text = location.text.copy(
                        end = element.children.firstOrNull()?.endOffset ?: location.text.end
                    )
                )
            }
            report(CodeSmell(issue, Entity.from(element, location), message))
        }
    }

    private fun KtTypeReference.fqNameOrNull(): FqName? =
        bindingContext[BindingContext.TYPE, this]?.fqNameOrNull()

    private fun KtExpression.expressionTypeOrNull(): KotlinType? =
        bindingContext[BindingContext.EXPRESSION_TYPE_INFO, this]?.type
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy