Skip to content

Commit

Permalink
Match extension functions
Browse files Browse the repository at this point in the history
  • Loading branch information
BraisGabin committed Jan 6, 2022
1 parent 1576eeb commit f73771a
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 5 deletions.
Expand Up @@ -48,7 +48,9 @@ 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."
"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)`"
)
private val methods: List<FunctionMatcher> by config(
listOf(
Expand Down
Expand Up @@ -318,5 +318,21 @@ class ForbiddenMethodCallSpec : Spek({
).compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
}

it("should report extension functions") {
val code = """
package org.example
fun String.bar() = Unit
fun foo() {
"".bar()
}
"""
val findings = ForbiddenMethodCall(
TestConfig(mapOf(METHODS to listOf("org.example.bar(kotlin.String)")))
).compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
}
}
})
Expand Up @@ -35,8 +35,9 @@ sealed class FunctionMatcher {
override fun match(callableDescriptor: CallableDescriptor): Boolean {
if (callableDescriptor.fqNameOrNull()?.asString() != fullyQualifiedName) return false

val encounteredParamTypes = callableDescriptor.valueParameters
.map { it.type.fqNameOrNull()?.asString() }
val encounteredParamTypes =
(listOfNotNull(callableDescriptor.extensionReceiverParameter) + callableDescriptor.valueParameters)
.map { it.type.fqNameOrNull()?.asString() }

return encounteredParamTypes == parameters
}
Expand All @@ -45,8 +46,9 @@ sealed class FunctionMatcher {
if (bindingContext == BindingContext.EMPTY) return false
if (function.name != fullyQualifiedName) return false

val encounteredParameters = function.valueParameters
.map { bindingContext[BindingContext.TYPE, it.typeReference]?.fqNameOrNull()?.toString() }
val encounteredParameters =
(listOfNotNull(function.receiverTypeReference) + function.valueParameters.map { it.typeReference })
.map { bindingContext[BindingContext.TYPE, it]?.fqNameOrNull()?.toString() }

return encounteredParameters == parameters
}
Expand Down
Expand Up @@ -171,6 +171,20 @@ private val matrixCase: Map<FunctionMatcher, Map<String, Boolean>> = run {
"fun foo(a: (String) -> Unit)" to true,
"fun foo(a: (Int) -> Unit)" to true,
),
FunctionMatcher.fromFunctionSignature("foo(kotlin.String)") to linkedMapOf(
"fun String.foo()" to true,
"fun foo(a: String)" to true,
"fun Int.foo()" to false,
"fun String.foo(a: Int)" to false,
"fun foo(a: String, ba: Int)" to false,
),
FunctionMatcher.fromFunctionSignature("foo(kotlin.String, kotlin.Int)") to linkedMapOf(
"fun String.foo()" to false,
"fun foo(a: String)" to false,
"fun Int.foo()" to false,
"fun String.foo(a: Int)" to true,
"fun foo(a: String, ba: Int)" to true,
),
)
}

Expand Down

0 comments on commit f73771a

Please sign in to comment.