From f53a497b6efe8aaebc699f34736b335e8db77c0e Mon Sep 17 00:00:00 2001 From: Peter Mandeljc Date: Wed, 27 Apr 2022 18:20:24 +0200 Subject: [PATCH 1/7] Fix CONTRIBUTING.md link --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b3d06b9107b..2d4eeafa886 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -10,7 +10,7 @@ - We use [JUnit 5](https://junit.org/junit5/docs/current/user-guide/) for testing. Please use the `Spec.kt` suffix on new test classes. If your new rule requires type resolution (i.e. it utilises `BindingContext`) then annotate your test class with `@KotlinCoreEnvironmentTest` and have the test class accept `KotlinCoreEnvironment` as a parameter. - See "Testing a rule that uses type resolution" section of the [Using Type Resolution](../docs/pages/gettingstarted/type-resolution.md) + See "Testing a rule that uses type resolution" section of the [Using Type Resolution](../website/docs/gettingstarted/type-resolution.md) guide for details. - Feel free to add your name to the contributors list at the end of the readme file when opening a pull request. - The code in `detekt-api` and any rule in `detekt-rules` must be documented. We generate documentation for our website based on these modules. From 995a48d48f4e15d8ef504759142514e6c087984f Mon Sep 17 00:00:00 2001 From: Peter Mandeljc Date: Thu, 28 Apr 2022 17:11:43 +0200 Subject: [PATCH 2/7] Add NestedScopeFunctions Rule --- .../main/resources/default-detekt-config.yml | 7 ++ .../rules/complexity/ComplexityProvider.kt | 1 + .../rules/complexity/NestedScopeFunctions.kt | 113 ++++++++++++++++++ .../complexity/NestedScopeFunctionsSpec.kt | 105 ++++++++++++++++ 4 files changed, 226 insertions(+) create mode 100644 detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt create mode 100644 detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt diff --git a/detekt-core/src/main/resources/default-detekt-config.yml b/detekt-core/src/main/resources/default-detekt-config.yml index f67bed5e645..3fe241bbc74 100644 --- a/detekt-core/src/main/resources/default-detekt-config.yml +++ b/detekt-core/src/main/resources/default-detekt-config.yml @@ -133,6 +133,13 @@ complexity: NestedBlockDepth: active: true threshold: 4 + NestedScopeFunctions: + active: false + threshold: 1 + functions: + - 'apply' + - 'run' + - 'with' ReplaceSafeCallChainWithRun: active: false StringLiteralDuplication: diff --git a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/ComplexityProvider.kt b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/ComplexityProvider.kt index 745e206e0c7..ff00e730793 100644 --- a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/ComplexityProvider.kt +++ b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/ComplexityProvider.kt @@ -24,6 +24,7 @@ class ComplexityProvider : DefaultRuleSetProvider { StringLiteralDuplication(config), MethodOverloading(config), NestedBlockDepth(config), + NestedScopeFunctions(config), TooManyFunctions(config), ComplexCondition(config), LabeledExpression(config), diff --git a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt new file mode 100644 index 00000000000..6b2584decb3 --- /dev/null +++ b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt @@ -0,0 +1,113 @@ +package io.gitlab.arturbosch.detekt.rules.complexity + +import io.gitlab.arturbosch.detekt.api.Config +import io.gitlab.arturbosch.detekt.api.Debt +import io.gitlab.arturbosch.detekt.api.DetektVisitor +import io.gitlab.arturbosch.detekt.api.Entity +import io.gitlab.arturbosch.detekt.api.Issue +import io.gitlab.arturbosch.detekt.api.Metric +import io.gitlab.arturbosch.detekt.api.Rule +import io.gitlab.arturbosch.detekt.api.Severity +import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell +import io.gitlab.arturbosch.detekt.api.config +import io.gitlab.arturbosch.detekt.api.internal.Configuration +import org.jetbrains.kotlin.psi.KtCallExpression +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression + +/** + * Although the scope functions are a way of making the code more concise, avoid overusing them: it can decrease + * your code readability and lead to errors. Avoid nesting scope functions and be careful when chaining them: + * it's easy to get confused about the current context object and the value of this or it. + * + * [Reference](https://kotlinlang.org/docs/scope-functions.html) + * + * + * // Try to figure out, what changed, without knowing the details + * first.apply { + * second.apply { + * b = a + * c = b + * } + * } + * + * + * + * // 'a' is a property of current class + * // 'b' is a property of class 'first' + * // 'c' is a property of class 'second' + * first.b = this.a + * second.c = first.b + * + */ +class NestedScopeFunctions(config: Config = Config.empty) : Rule(config) { + + override val issue = Issue( + javaClass.simpleName, + Severity.Maintainability, + "Over-using scope functions makes code confusing, hard to read and bug prone.", + Debt.FIVE_MINS + ) + + @Configuration("Number of nested scope functions allowed.") + private val threshold: Int by config(defaultValue = 1) + + @Configuration("Set of scope function names which add complexity.") + private val functions: Set by config(DEFAULT_FUNCTIONS) { it.toSet() } + + override fun visitNamedFunction(function: KtNamedFunction) { + function.accept(FunctionDepthVisitor()) + } + + private fun report(element: KtCallExpression, depth: Int) { + val finding = ThresholdedCodeSmell( + issue, + Entity.from(element), + Metric("SIZE", depth, threshold), + "The scope function '${element.calleeExpression?.text}' is nested too deeply ('$depth'). " + + "Complexity threshold is set to '$threshold'." + ) + report(finding) + } + + private companion object { + val DEFAULT_FUNCTIONS = listOf( + "apply", + "run", + "with", + ) + } + + private inner class FunctionDepthVisitor : DetektVisitor() { + private var depth = 0 + + override fun visitCallExpression(expression: KtCallExpression) { + fun callSuper() = super.visitCallExpression(expression) + + if (expression.isScopeFunction()) { + doWithIncrementedDepth { + reportIfOverThreshold(expression) + callSuper() + } + } else { + callSuper() + } + } + + private fun doWithIncrementedDepth(block: () -> Unit) { + depth++ + block() + depth-- + } + + private fun reportIfOverThreshold(expression: KtCallExpression) { + if (depth > threshold) { + report(expression, depth) + } + } + + private fun KtCallExpression.isScopeFunction(): Boolean = + getCallNameExpression()?.text?.let { functions.contains(it) } + ?: false + } +} diff --git a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt new file mode 100644 index 00000000000..b5d3598c825 --- /dev/null +++ b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt @@ -0,0 +1,105 @@ +package io.gitlab.arturbosch.detekt.rules.complexity + +import io.gitlab.arturbosch.detekt.api.Finding +import io.gitlab.arturbosch.detekt.test.TestConfig +import io.gitlab.arturbosch.detekt.test.assertThat +import io.gitlab.arturbosch.detekt.test.compileAndLint +import org.junit.jupiter.api.Test + +class NestedScopeFunctionsSpec { + + private var defaultConfig = TestConfig(mapOf("threshold" to 1, "functions" to listOf("run", "with"))) + private val subject = NestedScopeFunctions(defaultConfig) + + private lateinit var givenCode: String + private lateinit var actual: List + + @Test + fun `should report nesting`() { + givenCode = """ + fun f() { + 1.run { + 1.run { } + } + } + """ + whenLintRuns() + expectSourceLocation(3 to 11) + expectFunctionInMsg("run") + } + + @Test + fun `should report mixed nesting`() { + givenCode = """ + fun f() { + 1.run { + with(1) { } + } + } + """ + whenLintRuns() + expectSourceLocation(3 to 9) + expectFunctionInMsg("with") + } + + @Test + fun `should report when valid scope in between`() { + givenCode = """ + fun f() { + 1.run { + 1.valid { + with(1) { } + } + } + } + """ + whenLintRuns() + expectSourceLocation(4 to 13) + } + + @Test + fun `should not report in nested function`() { + givenCode = """ + fun f() { + 1.run { } + fun f2() { + 1.with { } + } + } + """ + whenLintRuns() + expectNoFindings() + } + + @Test + fun `should not report in neighboring scope functions`() { + givenCode = """ + fun f() { + 1.run { } + 1.run { } + with(1) {} + with(1) {} + } + """ + whenLintRuns() + expectNoFindings() + } + + private fun whenLintRuns() { + actual = subject.compileAndLint(givenCode) + } + + private fun expectSourceLocation(location: Pair) { + assertThat(actual).hasSourceLocation(location.first, location.second) + } + + private fun expectFunctionInMsg(scopeFunction: String) { + val expected = "The scope function '$scopeFunction' is nested too deeply ('2'). " + + "Complexity threshold is set to '1'." + assertThat(actual[0]).hasMessage(expected) + } + + private fun expectNoFindings() { + assertThat(actual).describedAs("findings size").isEmpty() + } +} From 83f2fa5647a1a026c7ec604ac447df36a1c4736b Mon Sep 17 00:00:00 2001 From: Peter Mandeljc Date: Sat, 30 Apr 2022 19:20:47 +0200 Subject: [PATCH 3/7] Fix NestedScopeFunctions CI errors --- .../detekt/rules/complexity/NestedScopeFunctions.kt | 2 +- .../detekt/rules/complexity/NestedScopeFunctionsSpec.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt index 6b2584decb3..bddf18e12e6 100644 --- a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt +++ b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt @@ -82,7 +82,7 @@ class NestedScopeFunctions(config: Config = Config.empty) : Rule(config) { private var depth = 0 override fun visitCallExpression(expression: KtCallExpression) { - fun callSuper() = super.visitCallExpression(expression) + fun callSuper(): Unit = super.visitCallExpression(expression) if (expression.isScopeFunction()) { doWithIncrementedDepth { diff --git a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt index b5d3598c825..86d1f7d12af 100644 --- a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt +++ b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt @@ -47,7 +47,7 @@ class NestedScopeFunctionsSpec { givenCode = """ fun f() { 1.run { - 1.valid { + "valid".apply { with(1) { } } } @@ -63,7 +63,7 @@ class NestedScopeFunctionsSpec { fun f() { 1.run { } fun f2() { - 1.with { } + with(1) { } } } """ From 67143a108c9ec8b415115324305a355f500cb98a Mon Sep 17 00:00:00 2001 From: Peter Mandeljc Date: Sat, 30 Apr 2022 20:32:20 +0200 Subject: [PATCH 4/7] Change NestedScopeFunctions Rule to fully-qualified names --- .../main/resources/default-detekt-config.yml | 6 +-- detekt-rules-complexity/build.gradle.kts | 1 + .../rules/complexity/NestedScopeFunctions.kt | 38 ++++++++++++++----- .../complexity/NestedScopeFunctionsSpec.kt | 16 ++++++-- 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/detekt-core/src/main/resources/default-detekt-config.yml b/detekt-core/src/main/resources/default-detekt-config.yml index 3fe241bbc74..a0a9314d356 100644 --- a/detekt-core/src/main/resources/default-detekt-config.yml +++ b/detekt-core/src/main/resources/default-detekt-config.yml @@ -137,9 +137,9 @@ complexity: active: false threshold: 1 functions: - - 'apply' - - 'run' - - 'with' + - 'kotlin.apply' + - 'kotlin.run' + - 'kotlin.with' ReplaceSafeCallChainWithRun: active: false StringLiteralDuplication: diff --git a/detekt-rules-complexity/build.gradle.kts b/detekt-rules-complexity/build.gradle.kts index 2db788f8ab8..60141b714c9 100644 --- a/detekt-rules-complexity/build.gradle.kts +++ b/detekt-rules-complexity/build.gradle.kts @@ -5,6 +5,7 @@ plugins { dependencies { compileOnly(projects.detektApi) compileOnly(projects.detektMetrics) + compileOnly(projects.detektTooling) testImplementation(projects.detektMetrics) testImplementation(projects.detektTest) testImplementation(libs.assertj) diff --git a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt index bddf18e12e6..293f1b4a1d3 100644 --- a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt +++ b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt @@ -1,5 +1,6 @@ package io.gitlab.arturbosch.detekt.rules.complexity +import io.github.detekt.tooling.api.FunctionMatcher import io.gitlab.arturbosch.detekt.api.Config import io.gitlab.arturbosch.detekt.api.Debt import io.gitlab.arturbosch.detekt.api.DetektVisitor @@ -11,9 +12,11 @@ import io.gitlab.arturbosch.detekt.api.Severity import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell import io.gitlab.arturbosch.detekt.api.config import io.gitlab.arturbosch.detekt.api.internal.Configuration +import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution +import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression +import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall /** * Although the scope functions are a way of making the code more concise, avoid overusing them: it can decrease @@ -40,6 +43,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression * second.c = first.b * */ +@RequiresTypeResolution class NestedScopeFunctions(config: Config = Config.empty) : Rule(config) { override val issue = Issue( @@ -52,8 +56,13 @@ class NestedScopeFunctions(config: Config = Config.empty) : Rule(config) { @Configuration("Number of nested scope functions allowed.") private val threshold: Int by config(defaultValue = 1) - @Configuration("Set of scope function names which add complexity.") - private val functions: Set by config(DEFAULT_FUNCTIONS) { it.toSet() } + @Configuration( + "Set of scope function names which add complexity. " + + "Function names have to be fully qualified. For example 'kotlin.apply'." + ) + private val functions: List by config(DEFAULT_FUNCTIONS) { + it.toSet().map(FunctionMatcher::fromFunctionSignature) + } override fun visitNamedFunction(function: KtNamedFunction) { function.accept(FunctionDepthVisitor()) @@ -72,9 +81,9 @@ class NestedScopeFunctions(config: Config = Config.empty) : Rule(config) { private companion object { val DEFAULT_FUNCTIONS = listOf( - "apply", - "run", - "with", + "kotlin.apply", + "kotlin.run", + "kotlin.with", ) } @@ -106,8 +115,19 @@ class NestedScopeFunctions(config: Config = Config.empty) : Rule(config) { } } - private fun KtCallExpression.isScopeFunction(): Boolean = - getCallNameExpression()?.text?.let { functions.contains(it) } - ?: false + private fun KtCallExpression.isScopeFunction(): Boolean { + val descriptors = resolveDescriptors() + val descriptor = descriptors.find { it.matchesScopeFunction() } + return descriptor != null + } + + private fun KtCallExpression.resolveDescriptors(): List = + getResolvedCall(bindingContext)?.resultingDescriptor + ?.let { listOf(it) + it.overriddenDescriptors } + ?: emptyList() + + private fun CallableDescriptor.matchesScopeFunction(): Boolean { + return functions.find { it.match(this) } != null + } } } diff --git a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt index 86d1f7d12af..d07c14a16bb 100644 --- a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt +++ b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt @@ -1,14 +1,22 @@ package io.gitlab.arturbosch.detekt.rules.complexity import io.gitlab.arturbosch.detekt.api.Finding +import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat -import io.gitlab.arturbosch.detekt.test.compileAndLint +import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.junit.jupiter.api.Test -class NestedScopeFunctionsSpec { +@KotlinCoreEnvironmentTest +class NestedScopeFunctionsSpec(private val env: KotlinCoreEnvironment) { - private var defaultConfig = TestConfig(mapOf("threshold" to 1, "functions" to listOf("run", "with"))) + private var defaultConfig = TestConfig( + mapOf( + "threshold" to 1, + "functions" to listOf("kotlin.run", "kotlin.with") + ) + ) private val subject = NestedScopeFunctions(defaultConfig) private lateinit var givenCode: String @@ -86,7 +94,7 @@ class NestedScopeFunctionsSpec { } private fun whenLintRuns() { - actual = subject.compileAndLint(givenCode) + actual = subject.compileAndLintWithContext(env, givenCode) } private fun expectSourceLocation(location: Pair) { From bac48f79593aa99baae00bee5421b9342b90dd51 Mon Sep 17 00:00:00 2001 From: Peter Mandeljc Date: Sat, 30 Apr 2022 20:34:31 +0200 Subject: [PATCH 5/7] Add NestedScopeFunctions 'let' and 'also' to default --- detekt-core/src/main/resources/default-detekt-config.yml | 2 ++ .../arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/detekt-core/src/main/resources/default-detekt-config.yml b/detekt-core/src/main/resources/default-detekt-config.yml index a0a9314d356..8cbfa2c079c 100644 --- a/detekt-core/src/main/resources/default-detekt-config.yml +++ b/detekt-core/src/main/resources/default-detekt-config.yml @@ -140,6 +140,8 @@ complexity: - 'kotlin.apply' - 'kotlin.run' - 'kotlin.with' + - 'kotlin.let' + - 'kotlin.also' ReplaceSafeCallChainWithRun: active: false StringLiteralDuplication: diff --git a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt index 293f1b4a1d3..61263d20450 100644 --- a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt +++ b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt @@ -84,6 +84,8 @@ class NestedScopeFunctions(config: Config = Config.empty) : Rule(config) { "kotlin.apply", "kotlin.run", "kotlin.with", + "kotlin.let", + "kotlin.also", ) } From d5368cbfeb910f48c147df8971f62e4c601d1afe Mon Sep 17 00:00:00 2001 From: Peter Mandeljc Date: Sun, 1 May 2022 15:14:28 +0200 Subject: [PATCH 6/7] Change NestedScopeFunctions to early return on empty context --- .../rules/complexity/NestedScopeFunctions.kt | 2 ++ .../complexity/NestedScopeFunctionsSpec.kt | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt index 61263d20450..dbddc9f1da7 100644 --- a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt +++ b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt @@ -16,6 +16,7 @@ import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall /** @@ -65,6 +66,7 @@ class NestedScopeFunctions(config: Config = Config.empty) : Rule(config) { } override fun visitNamedFunction(function: KtNamedFunction) { + if (bindingContext == BindingContext.EMPTY) return function.accept(FunctionDepthVisitor()) } diff --git a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt index d07c14a16bb..b3f4be087ba 100644 --- a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt +++ b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt @@ -4,6 +4,7 @@ import io.gitlab.arturbosch.detekt.api.Finding import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest import io.gitlab.arturbosch.detekt.test.TestConfig import io.gitlab.arturbosch.detekt.test.assertThat +import io.gitlab.arturbosch.detekt.test.compileAndLint import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.junit.jupiter.api.Test @@ -93,10 +94,27 @@ class NestedScopeFunctionsSpec(private val env: KotlinCoreEnvironment) { expectNoFindings() } + @Test + fun `should not report when binding context is empty`() { + givenCode = """ + fun f() { + 1.run { + 1.run { } + } + } + """ + whenLintRunsWithoutContext() + expectNoFindings() + } + private fun whenLintRuns() { actual = subject.compileAndLintWithContext(env, givenCode) } + private fun whenLintRunsWithoutContext() { + actual = subject.compileAndLint(givenCode) + } + private fun expectSourceLocation(location: Pair) { assertThat(actual).hasSourceLocation(location.first, location.second) } From 1acbf180534fe37ea5d06910c8357cecfe26a4ca Mon Sep 17 00:00:00 2001 From: Peter Mandeljc Date: Mon, 2 May 2022 13:08:26 +0200 Subject: [PATCH 7/7] Fix NestedScopeFunctions detekt errors --- .../detekt/rules/complexity/NestedScopeFunctions.kt | 10 ++++------ .../rules/complexity/NestedScopeFunctionsSpec.kt | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt index dbddc9f1da7..81a0dc39d7f 100644 --- a/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt +++ b/detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctions.kt @@ -121,17 +121,15 @@ class NestedScopeFunctions(config: Config = Config.empty) : Rule(config) { private fun KtCallExpression.isScopeFunction(): Boolean { val descriptors = resolveDescriptors() - val descriptor = descriptors.find { it.matchesScopeFunction() } - return descriptor != null + return !descriptors.any { it.matchesScopeFunction() } } private fun KtCallExpression.resolveDescriptors(): List = getResolvedCall(bindingContext)?.resultingDescriptor ?.let { listOf(it) + it.overriddenDescriptors } - ?: emptyList() + .orEmpty() - private fun CallableDescriptor.matchesScopeFunction(): Boolean { - return functions.find { it.match(this) } != null - } + private fun CallableDescriptor.matchesScopeFunction(): Boolean = + !functions.any { it.match(this) } } } diff --git a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt index b3f4be087ba..df1817d52b5 100644 --- a/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt +++ b/detekt-rules-complexity/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/NestedScopeFunctionsSpec.kt @@ -12,7 +12,7 @@ import org.junit.jupiter.api.Test @KotlinCoreEnvironmentTest class NestedScopeFunctionsSpec(private val env: KotlinCoreEnvironment) { - private var defaultConfig = TestConfig( + private val defaultConfig = TestConfig( mapOf( "threshold" to 1, "functions" to listOf("kotlin.run", "kotlin.with")