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..6486d326e9c 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 @@ -30,9 +30,9 @@ import org.jetbrains.kotlin.resolve.calls.util.getCalleeExpressionIfAny import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall /** - * This rule allows to set a list of forbidden methods. This can be used to discourage the use of unstable, experimental - * or deprecated methods, especially for methods imported from external libraries. - * Detekt will then report all method invocations that are forbidden. + * This rule allows to set a list of forbidden methods or constructors. This can be used to discourage the use + * of unstable, experimental or deprecated methods, especially for methods imported from external libraries. + * Detekt will then report all method or constructor invocations that are forbidden. * * * import java.lang.System @@ -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)