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

Fixed bug reporting false positives with EmptyFunctionBlock #1690

Merged
merged 5 commits into from Jun 17, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
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 @@ -7,9 +7,13 @@ import org.jetbrains.kotlin.psi.KtNamedFunction

/**
* Reports empty functions. Empty blocks of code serve no purpose and should be removed.
* This rule will not report functions overriding others.
* This rule will not report functions with the override modifier that have a comment as their only body contents
* (e.g., a // no-op comment in an unused listener function).
*
* @configuration ignoreOverriddenFunctions - excludes overridden functions with an empty body (default: `false`)
* Set the [ignoreOverriddenFunctions] parameter to `true` to exclude all functions which are overriding other
* functions from the superclass or from an interface (i.e., functions declared with the override modifier).
*
* @configuration ignoreOverriddenFunctions - Excludes all the overridden functions (default: `false`)
*
* @active since v1.0.0
* @author Artur Bosch
Expand Down
Expand Up @@ -33,10 +33,14 @@ abstract class EmptyRule(config: Config) : Rule(config) {
checkBlockExpr(true)
}

private fun KtExpression.checkBlockExpr(hasComment: Boolean) {
private fun KtExpression.checkBlockExpr(skipIfCommented: Boolean = false) {
val blockExpression = this.asBlockExpression()
blockExpression?.statements?.let {
if (it.isEmpty() && blockExpression.hasCommentInside() == hasComment) {
val hasComment = blockExpression.hasCommentInside()
if (skipIfCommented && hasComment) {
return
}
if (it.isEmpty() && !hasComment) {
report(CodeSmell(issue, Entity.from(this), "This empty block of code can be removed."))
}
}
Expand Down
@@ -1,9 +1,10 @@
package io.gitlab.arturbosch.detekt.rules.empty

import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.test.TEST_FILENAME
import io.gitlab.arturbosch.detekt.test.TestConfig
import io.gitlab.arturbosch.detekt.test.assertThat
import io.gitlab.arturbosch.detekt.test.compileAndLint
import org.assertj.core.api.Assertions.assertThat
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe

Expand All @@ -13,6 +14,8 @@ import org.spekframework.spek2.style.specification.describe
*/
class EmptyFunctionBlockSpec : Spek({

val fileName = TEST_FILENAME

val subject by memoized { EmptyFunctionBlock(Config.empty) }

describe("EmptyFunctionBlock rule") {
Expand All @@ -22,7 +25,7 @@ class EmptyFunctionBlockSpec : Spek({
class A {
protected fun stuff() {}
}"""
assertThat(subject.compileAndLint(code)).hasSize(1)
assertThat(subject.compileAndLint(code)).hasLocationStrings("'{}' at (2,27) in /$fileName")
cortinico marked this conversation as resolved.
Show resolved Hide resolved
}

it("should not flag function with open modifier") {
Expand All @@ -38,7 +41,7 @@ class EmptyFunctionBlockSpec : Spek({
fun a() {
fun b() {}
}"""
assertThat(subject.compileAndLint(code)).hasSize(1)
assertThat(subject.compileAndLint(code)).hasLocationStrings("'{}' at (2,10) in /$fileName")
cortinico marked this conversation as resolved.
Show resolved Hide resolved
}

context("some overridden functions") {
Expand Down Expand Up @@ -72,7 +75,36 @@ class EmptyFunctionBlockSpec : Spek({

it("should not flag overridden functions") {
val config = TestConfig(mapOf(EmptyFunctionBlock.IGNORE_OVERRIDDEN_FUNCTIONS to "true"))
assertThat(EmptyFunctionBlock(config).compileAndLint(code)).hasSize(1)
assertThat(EmptyFunctionBlock(config).compileAndLint(code)).hasLocationStrings("'{}' at (1,13) in /$fileName")
cortinico marked this conversation as resolved.
Show resolved Hide resolved
}
}

context("some overridden functions") {
val code = """
private interface Listener {
fun listenThis()

fun listenThat()
}

private interface AnimationEndListener : Listener {
override fun listenThis() {
// no-op
}

override fun listenThat() {

}
}
""".trimIndent()
it("should not flag overridden functions with commented body") {
assertThat(subject.compileAndLint(code))
cortinico marked this conversation as resolved.
Show resolved Hide resolved
.hasLocationStrings("'{\n\n }' at (12,31) in /$fileName")
}

it("should not flag overridden functions with ignoreOverriddenFunctions") {
val config = TestConfig(mapOf(EmptyFunctionBlock.IGNORE_OVERRIDDEN_FUNCTIONS to "true"))
assertThat(EmptyFunctionBlock(config).compileAndLint(code)).isEmpty()
}
}
}
Expand Down
4 changes: 0 additions & 4 deletions detekt-rules/src/test/resources/cases/Empty.kt
Expand Up @@ -19,10 +19,6 @@ class Empty : Runnable {

}

fun emptyMethod() {
cortinico marked this conversation as resolved.
Show resolved Hide resolved

}

fun stuff() {
try {

Expand Down
Expand Up @@ -65,7 +65,9 @@ object KtTestCompiler : KtCompiler() {
fun createPsiFactory(): KtPsiFactory = KtPsiFactory(KtTestCompiler.environment.project, false)

class TestDisposable : Disposable {
override fun dispose() {} // Don't want to dispose the test KotlinCoreEnvironment
override fun dispose() {
// Don't want to dispose the test KotlinCoreEnvironment
}
}
}

Expand Down
8 changes: 6 additions & 2 deletions docs/pages/documentation/empty-blocks.md
Expand Up @@ -74,7 +74,11 @@ Reports empty `for` loops. Empty blocks of code serve no purpose and should be r
### EmptyFunctionBlock

Reports empty functions. Empty blocks of code serve no purpose and should be removed.
This rule will not report functions overriding others.
This rule will not report functions with the override modifier that have a comment as their only body contents
(e.g., a // no-op comment in an unused listener function).

Set the [ignoreOverriddenFunctions] parameter to `true` to exclude all functions which are overriding other
functions from the superclass or from an interface (i.e., functions declared with the override modifier).

**Severity**: Minor

Expand All @@ -84,7 +88,7 @@ This rule will not report functions overriding others.

* `ignoreOverriddenFunctions` (default: `false`)

excludes overridden functions with an empty body
Excludes all the overridden functions

### EmptyIfBlock

Expand Down