Skip to content

Commit

Permalink
NamedArguments: Ignore when argument values are the same as the param…
Browse files Browse the repository at this point in the history
…eter name (#4613)

* NamedArguments: Ignore when argument values are the same as the parameter names

* Add tests
  • Loading branch information
t-kameyama committed Mar 10, 2022
1 parent 3e895d6 commit 1269b9b
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 4 deletions.
1 change: 1 addition & 0 deletions detekt-core/src/main/resources/default-detekt-config.yml
Expand Up @@ -129,6 +129,7 @@ complexity:
NamedArguments:
active: false
threshold: 3
ignoreArgumentsMatchingNames: false
NestedBlockDepth:
active: true
threshold: 4
Expand Down
Expand Up @@ -44,6 +44,9 @@ class NamedArguments(config: Config = Config.empty) : Rule(config) {
@Configuration("number of parameters that triggers this inspection")
private val threshold: Int by config(defaultValue = 3)

@Configuration("ignores when argument values are the same as the parameter names")
private val ignoreArgumentsMatchingNames: Boolean by config(defaultValue = false)

override fun visitCallExpression(expression: KtCallExpression) {
if (bindingContext == BindingContext.EMPTY) return
val valueArguments = expression.valueArguments
Expand All @@ -56,12 +59,21 @@ class NamedArguments(config: Config = Config.empty) : Rule(config) {

@Suppress("ReturnCount")
private fun KtCallExpression.canNameArguments(): Boolean {
val unnamedArguments = valueArguments.filterNot { it.isNamed() || it is KtLambdaArgument }
if (unnamedArguments.isEmpty()) return false
val resolvedCall = getResolvedCall(bindingContext) ?: return false
if (!resolvedCall.candidateDescriptor.hasStableParameterNames()) return false
return unnamedArguments.all {
resolvedCall.getParameterForArgument(it)?.varargElementType == null || it.getSpreadElement() != null

val unnamedArguments = valueArguments.mapNotNull { argument ->
if (argument.isNamed() || argument is KtLambdaArgument) return@mapNotNull null
val parameter = resolvedCall.getParameterForArgument(argument) ?: return@mapNotNull null
if (ignoreArgumentsMatchingNames &&
parameter.name.asString() == argument.getArgumentExpression()?.text
) return@mapNotNull null
argument to parameter
}
if (unnamedArguments.isEmpty()) return false

return unnamedArguments.all { (argument, parameter) ->
argument.getSpreadElement() != null || parameter.varargElementType == null
}
}
}
Expand Up @@ -200,5 +200,81 @@ class NamedArgumentsSpec(val env: KotlinCoreEnvironment) {
assertThat(findings).hasSize(1)
}
}

@Nested
inner class IgnoreArgumentsMatchingNames {
@Nested
inner class `ignoreArgumentsMatchingNames is true` {
val subject =
NamedArguments(TestConfig(mapOf("threshold" to 2, "ignoreArgumentsMatchingNames" to true)))

@Test
fun `all arguments are the same as the parameter names`() {
val code = """
fun foo(a: Int, b: Int, c: Int) {}
fun bar(a: Int, b: Int, c: Int) {
foo(a, b, c)
}
"""
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(0)
}

@Test
fun `some arguments are not the same as the parameter names`() {
val code = """
fun foo(a: Int, b: Int, c: Int) {}
fun bar(a: Int, b: Int, c: Int) {
foo(a, c, b)
}
"""
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
}

@Test
fun `all arguments are the same as the parameter names and have a this receiver`() {
val code = """
class Baz {
private var b: Int = 42
fun foo(a: Int, b: Int, c: Int) {}
fun bar(a: Int, c: Int) {
foo(a, this.b, c)
}
}
"""
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
}

@Test
fun `all arguments are the same as the parameter names and have a it receiver`() {
val code = """
data class Baz(val b: Int)
fun foo(a: Int, b: Int, c: Int) {}
fun bar(a: Int, c: Int, baz: Baz?) {
baz?.let { foo(a, it.b, c) }
}
"""
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
}
}

@Nested
inner class `ignoreArgumentsMatchingNames is false` {
@Test
fun `all arguments are the same as parameter names`() {
val code = """
fun foo(a: Int, b: Int, c: Int) {}
fun bar(a: Int, b: Int, c: Int) {
foo(a, b, c)
}
"""
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
}
}
}
}
}

0 comments on commit 1269b9b

Please sign in to comment.