diff --git a/detekt-rules-ruleauthors/src/main/kotlin/io/gitlab/arturbosch/detekt/authors/RequiresTypeResolution.kt b/detekt-rules-ruleauthors/src/main/kotlin/io/gitlab/arturbosch/detekt/authors/RequiresTypeResolution.kt index dafba19a67c0..f602c2715ac7 100644 --- a/detekt-rules-ruleauthors/src/main/kotlin/io/gitlab/arturbosch/detekt/authors/RequiresTypeResolution.kt +++ b/detekt-rules-ruleauthors/src/main/kotlin/io/gitlab/arturbosch/detekt/authors/RequiresTypeResolution.kt @@ -14,7 +14,9 @@ import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution import io.gitlab.arturbosch.detekt.rules.fqNameOrNull import org.jetbrains.kotlin.psi.KtClass -import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNameReferenceExpression +import org.jetbrains.kotlin.psi.KtReferenceExpression import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.types.typeUtil.supertypes import kotlin.reflect.KClass @@ -33,11 +35,12 @@ class RequiresTypeResolution(config: Config = Config.empty) : Rule(config) { Debt.FIVE_MINS ) - override fun visitClass(klass: KtClass) { - super.visitClass(klass) + private val klasses: MutableList = mutableListOf() + private var usesBindingContext: Boolean = false - if (klass.extendsFrom(BaseRule::class)) { - val usesBindingContext = usesBindingContext(klass) + override fun visitKtFile(file: KtFile) { + super.visitKtFile(file) + klasses.forEach { klass -> val isAnnotatedWithRequiresTypeResolution = klass.isAnnotatedWith(RequiresTypeResolution::class) if (usesBindingContext && !isAnnotatedWithRequiresTypeResolution) { report( @@ -58,10 +61,20 @@ class RequiresTypeResolution(config: Config = Config.empty) : Rule(config) { } } } -} -private fun usesBindingContext(element: KtElement): Boolean { - return element.containingKtFile.text.contains("bindingContext", ignoreCase = false) + override fun visitClass(klass: KtClass) { + super.visitClass(klass) + + if (klass.extendsFrom(BaseRule::class)) { + klasses.add(klass) + } + } + + override fun visitReferenceExpression(expression: KtReferenceExpression) { + super.visitReferenceExpression(expression) + usesBindingContext = usesBindingContext || + (expression is KtNameReferenceExpression && expression.text == "bindingContext") + } } context(BaseRule) private inline fun KtClass.extendsFrom(kClass: KClass): Boolean { diff --git a/detekt-rules-ruleauthors/src/test/kotlin/io/gitlab/arturbosch/detekt/authors/RequiresTypeResolutionSpec.kt b/detekt-rules-ruleauthors/src/test/kotlin/io/gitlab/arturbosch/detekt/authors/RequiresTypeResolutionSpec.kt index 1c08e52bc9e8..6350b57da7ca 100644 --- a/detekt-rules-ruleauthors/src/test/kotlin/io/gitlab/arturbosch/detekt/authors/RequiresTypeResolutionSpec.kt +++ b/detekt-rules-ruleauthors/src/test/kotlin/io/gitlab/arturbosch/detekt/authors/RequiresTypeResolutionSpec.kt @@ -89,4 +89,27 @@ internal class RequiresTypeResolutionSpec(private val env: KotlinCoreEnvironment val findings = rule.compileAndLintWithContext(env, code) assertThat(findings).hasSize(1) } + + @Test + fun `should report Rules that use bindingContext outside class and are not annotated`() { + val code = """ + import io.gitlab.arturbosch.detekt.api.Config + import io.gitlab.arturbosch.detekt.api.Rule + + class A(config: Config) : Rule(config) { + override val issue = error("I don't care") + + private fun asdf() { + extension() + } + } + + private inline fun Rule.extension(): Boolean { + bindingContext + return true + } + """ + val findings = rule.compileAndLintWithContext(env, code) + assertThat(findings).hasSize(1) + } }