From a0a8101e9b794a225c1cd59c4e0e87bd8707450d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brais=20Gab=C3=ADn?= Date: Sun, 7 Aug 2022 13:06:06 +0200 Subject: [PATCH] Allow to forbid constructors with ForbiddenMethodCall --- .../detekt/rules/style/ForbiddenMethodCall.kt | 3 +- .../rules/style/ForbiddenMethodCallSpec.kt | 68 +++++++++++++++++++ .../detekt/tooling/api/FunctionMatcher.kt | 5 +- 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCall.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCall.kt index a49cfb05eb2..6be710b66aa 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCall.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCall.kt @@ -61,7 +61,8 @@ class ForbiddenMethodCall(config: Config = Config.empty) : Rule(config) { "(i.e. `java.time.LocalDate(java.time.Clock)`) which would report only call " + "with this concrete signature. If you want to forbid an extension function like" + "`fun String.hello(a: Int)` you should add the receiver parameter as the first parameter like this: " + - "`hello(kotlin.String, kotlin.Int)`" + "`hello(kotlin.String, kotlin.Int)`. To forbid constructor calls you need to define them with `` " + + " for example `java.util.Date.`." ) private val methods: List by config( valuesWithReason( diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCallSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCallSpec.kt index 1102c0eb9fb..854e9d2d165 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCallSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCallSpec.kt @@ -508,4 +508,72 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { ) assertThat(findings).hasSize(1) } + + @Nested + inner class `Forbid constructors` { + @Nested + inner class NameOnly { + @Test + fun `empty constructor`() { + val code = """ + import java.util.Date + + val a = Date() + """.trimIndent() + val findings = + ForbiddenMethodCall(TestConfig(METHODS to listOf("java.util.Date."))).compileAndLintWithContext( + env, + code + ) + assertThat(findings).hasSize(1) + } + + @Test + fun `no-empty constructor`() { + val code = """ + import java.util.Date + + val a = Date(2022, 8 ,7) + """.trimIndent() + val findings = + ForbiddenMethodCall(TestConfig(METHODS to listOf("java.util.Date."))).compileAndLintWithContext( + env, + code + ) + assertThat(findings).hasSize(1) + } + } + + @Nested + inner class WithParameters { + @Test + fun `empty constructor`() { + val code = """ + import java.util.Date + + val a = Date() + """.trimIndent() + val findings = + ForbiddenMethodCall(TestConfig(METHODS to listOf("java.util.Date.()"))).compileAndLintWithContext( + env, + code + ) + assertThat(findings).hasSize(1) + } + + @Test + fun `no-empty constructor`() { + val code = """ + import java.util.Date + + val a = Date(2022, 8 ,7) + """.trimIndent() + val findings = + ForbiddenMethodCall( + TestConfig(METHODS to listOf("java.util.Date.(kotlin.Int, kotlin.Int, kotlin.Int)")) + ).compileAndLintWithContext(env, code) + assertThat(findings).hasSize(1) + } + } + } } diff --git a/detekt-tooling/src/main/kotlin/io/github/detekt/tooling/api/FunctionMatcher.kt b/detekt-tooling/src/main/kotlin/io/github/detekt/tooling/api/FunctionMatcher.kt index 37de32c7b1d..18b4dfce5bc 100644 --- a/detekt-tooling/src/main/kotlin/io/github/detekt/tooling/api/FunctionMatcher.kt +++ b/detekt-tooling/src/main/kotlin/io/github/detekt/tooling/api/FunctionMatcher.kt @@ -5,6 +5,7 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isTypeParameter @@ -18,7 +19,7 @@ sealed class FunctionMatcher { private val fullyQualifiedName: String ) : FunctionMatcher() { override fun match(callableDescriptor: CallableDescriptor): Boolean { - return callableDescriptor.fqNameOrNull()?.asString() == fullyQualifiedName + return callableDescriptor.fqNameSafe.asString() == fullyQualifiedName } override fun match(function: KtNamedFunction, bindingContext: BindingContext): Boolean { @@ -36,7 +37,7 @@ sealed class FunctionMatcher { ) : FunctionMatcher() { override fun match(callableDescriptor: CallableDescriptor): Boolean { val descriptor = callableDescriptor.original - if (descriptor.fqNameOrNull()?.asString() != fullyQualifiedName) return false + if (descriptor.fqNameSafe.asString() != fullyQualifiedName) return false val encounteredParamTypes = (listOfNotNull(descriptor.extensionReceiverParameter) + descriptor.valueParameters)