Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forbid constructors with ForbiddenMethodCall #5195

Merged
merged 3 commits into from Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -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 `<init>` " +
BraisGabin marked this conversation as resolved.
Show resolved Hide resolved
" for example `java.util.Date.<init>`."
)
private val methods: List<Forbidden> by config(
valuesWithReason(
Expand Down
Expand Up @@ -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.<init>"))).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.<init>"))).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.<init>()"))).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.<init>(kotlin.Int, kotlin.Int, kotlin.Int)"))
).compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
}
}
}
}
Expand Up @@ -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

Expand All @@ -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 {
Expand All @@ -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)
Expand Down