From 621bcc912114f22d93c300ed1ec9f40fa919406b Mon Sep 17 00:00:00 2001 From: Dominic Zirbel Date: Thu, 3 Nov 2022 08:41:43 -0700 Subject: [PATCH] Document and test edge cases for ForbiddenMethodCall function signatures (#5495) I was stuck trying to match some function signatures (from Timber on Android) for the ForbiddenMethodCall rule and had to test inside detekt to figure out how to do it properly. To make this more clear in the future, I've expanded the description of the methods configuration parameter and added more tests for these cases. Some of this might be obvious to some but it was not to me at the time so I think having some quick tips might be helpful; or perhaps a link to a more comprehensive guide on Kotlin function signatures (if one exists). --- .../detekt/rules/style/ForbiddenMethodCall.kt | 7 +- .../rules/style/ForbiddenMethodCallSpec.kt | 94 +++++++++++++++++++ 2 files changed, 99 insertions(+), 2 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 88d43e5cb55..495e9deabf3 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 @@ -58,10 +58,13 @@ class ForbiddenMethodCall(config: Config = Config.empty) : Rule(config) { "Methods can be defined without full signature (i.e. `java.time.LocalDate.now`) which will report " + "calls of all methods with this name or with full signature " + "(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" + + "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)`. To forbid constructor calls you need to define them with ``, " + - " for example `java.util.Date.`." + "for example `java.util.Date.`. To forbid calls involving type parameters, omit them, for example " + + "`fun hello(args: Array)` is referred to as simply `hello(kotlin.Array)` (also the signature for " + + "vararg parameters). To forbid methods from the companion object reference the Companion class, for " + + "example as `TestClass.Companion.hello()` (even if it is marked `@JvmStatic`)." ) 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 dadc3f2d679..7d956fa1592 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 @@ -283,6 +283,100 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { assertThat(findings).hasStartSourceLocation(6, 13) } + @Test + fun `should report method with array argument`() { + val code = """ + package io.gitlab.arturbosch.detekt.rules.style + + fun arrayMethod(args: Array) = args.size + + fun test() { + val s = arrayMethod(arrayOf("test")) + } + """.trimIndent() + val methodName = "io.gitlab.arturbosch.detekt.rules.style.arrayMethod(kotlin.Array)" + val findings = ForbiddenMethodCall(TestConfig(mapOf(METHODS to listOf(methodName)))) + .compileAndLintWithContext(env, code) + assertThat(findings).hasSize(1).hasStartSourceLocation(6, 13) + } + + @Test + fun `should report method with list argument`() { + val code = """ + package io.gitlab.arturbosch.detekt.rules.style + + fun listMethod(args: List) = args.size + + fun test() { + val s = listMethod(listOf("test")) + } + """.trimIndent() + val methodName = "io.gitlab.arturbosch.detekt.rules.style.listMethod(kotlin.collections.List)" + val findings = ForbiddenMethodCall(TestConfig(mapOf(METHODS to listOf(methodName)))) + .compileAndLintWithContext(env, code) + assertThat(findings).hasSize(1).hasStartSourceLocation(6, 13) + } + + @Test + fun `should report method with vararg argument`() { + val code = """ + package io.gitlab.arturbosch.detekt.rules.style + + fun varargMethod(vararg args: Any) = args.size + + fun test() { + val s = varargMethod(arrayOf("test")) + } + """.trimIndent() + val methodName = "io.gitlab.arturbosch.detekt.rules.style.varargMethod(kotlin.Array)" + val findings = ForbiddenMethodCall(TestConfig(mapOf(METHODS to listOf(methodName)))) + .compileAndLintWithContext(env, code) + assertThat(findings).hasSize(1).hasStartSourceLocation(6, 13) + } + + @Test + fun `should report companion object method`() { + val code = """ + package io.gitlab.arturbosch.detekt.rules.style + + class TestClass { + companion object { + fun staticMethod() {} + } + } + + fun test() { + TestClass.staticMethod() + } + """.trimIndent() + val methodName = "io.gitlab.arturbosch.detekt.rules.style.TestClass.Companion.staticMethod()" + val findings = ForbiddenMethodCall(TestConfig(mapOf(METHODS to listOf(methodName)))) + .compileAndLintWithContext(env, code) + assertThat(findings).hasSize(1).hasStartSourceLocation(10, 15) + } + + @Test + fun `should report @JvmStatic method`() { + val code = """ + package io.gitlab.arturbosch.detekt.rules.style + + class TestClass { + companion object { + @JvmStatic + fun staticMethod() {} + } + } + + fun test() { + TestClass.staticMethod() + } + """.trimIndent() + val methodName = "io.gitlab.arturbosch.detekt.rules.style.TestClass.Companion.staticMethod()" + val findings = ForbiddenMethodCall(TestConfig(mapOf(METHODS to listOf(methodName)))) + .compileAndLintWithContext(env, code) + assertThat(findings).hasSize(1).hasStartSourceLocation(11, 15) + } + @Test fun `should report overriding method calls`() { val code = """