Skip to content

Commit

Permalink
Passing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
joeldickson committed Oct 12, 2024
1 parent 4edcb91 commit c5bb9b2
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 45 deletions.
7 changes: 4 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,16 @@ repositories {
dependencies {
implementation(kotlin("stdlib"))
implementation("com.pinterest.ktlint:ktlint-core:0.48.2")
implementation("io.gitlab.arturbosch.detekt:detekt-api:1.22.0")
implementation("io.gitlab.arturbosch.detekt:detekt-api:1.23.0")
implementation("com.fasterxml.jackson.core:jackson-databind:2.13.3")

testImplementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:1.5.0")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testImplementation("io.gitlab.arturbosch.detekt:detekt-test:1.22.0")
testImplementation("io.gitlab.arturbosch.detekt:detekt-test:1.23.0")
testImplementation("org.mockito:mockito-core:4.8.1")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.1")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.1")
testImplementation("org.assertj:assertj-core:3.21.0")
testImplementation("org.assertj:assertj-core:3.24.2")
}

tasks.test {
Expand Down
81 changes: 55 additions & 26 deletions src/main/kotlin/io/agodadev/kraftdetekt/IgnoredReturnValueRule.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package io.agodadev.kraftdetekt

import io.gitlab.arturbosch.detekt.api.*
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.isNothing
import org.jetbrains.kotlin.types.checker.SimpleClassicTypeSystemContext.isUnit
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit

class IgnoredReturnValueRule(config: Config) : Rule(config) {
override val issue = Issue(
Expand All @@ -19,29 +18,59 @@ class IgnoredReturnValueRule(config: Config) : Rule(config) {
override fun visitCallExpression(expression: KtCallExpression) {
super.visitCallExpression(expression)

val parent = expression.parent
if (parent is KtExpression && !parent.isUsedAsExpression(bindingContext)) {
val resolvedCall = expression.getResolvedCall(bindingContext) ?: return
val returnType = resolvedCall.resultingDescriptor.returnType ?: return

if (!returnType.isUnit() && !returnType.isNothing()) {
report(CodeSmell(
issue,
Entity.from(expression),
"The return value of this function call is ignored."
))
}
println("Visiting call expression: ${expression.text}")

val resolvedCall = expression.getResolvedCall(bindingContext)
if (resolvedCall == null) {
println(" Resolved call is null")
return
}
}
}

class IgnoredReturnValueRuleProvider : RuleSetProvider {
override val ruleSetId: String = "custom-rules"
val returnType = resolvedCall.resultingDescriptor.returnType
if (returnType == null) {
println(" Return type is null")
return
}

println(" Return type: $returnType")
println(" Is Unit: ${returnType.isUnit()}")
println(" Is Nothing: ${returnType.isNothing()}")

if (!returnType.isUnit() && !returnType.isNothing()) {
val parent = expression.parent
println(" Parent: ${parent?.javaClass?.simpleName}")

override fun instance(config: Config): RuleSet {
return RuleSet(
ruleSetId,
listOf(IgnoredReturnValueRule(config))
)
when {
parent is KtValueArgument -> println(" Parent is KtValueArgument")
parent is KtProperty -> println(" Parent is KtProperty")
parent is KtReturnExpression -> println(" Parent is KtReturnExpression")
parent is KtBinaryExpression && parent.operationToken.toString() == "EQ" -> println(" Parent is assignment")
parent is KtIfExpression && expression == parent.condition -> println(" Parent is if condition")
parent is KtWhenConditionWithExpression -> println(" Parent is when condition")
parent is KtBinaryExpression && parent.operationToken.toString() in setOf("GT", "LT", "GTEQ", "LTEQ", "EQEQ", "EXCLEQ") -> println(" Parent is comparison")
parent is KtDotQualifiedExpression -> println(" Parent is dot qualified expression")
parent is KtBlockExpression -> {
println(" Parent is block expression")
if (parent.statements.lastOrNull() != expression) {
println(" Reporting issue: ignored return value in block")
report(CodeSmell(
issue,
Entity.from(expression),
"The return value of this function call is ignored."
))
}
}
else -> {
println(" Reporting issue: general case")
report(CodeSmell(
issue,
Entity.from(expression),
"The return value of this function call is ignored."
))
}
}
} else {
println(" Not reporting: return type is Unit or Nothing")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,68 @@
package io.agodadev.kraftdetekt

import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.test.compileAndLint
import io.gitlab.arturbosch.detekt.test.TestConfig
import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext
import io.github.detekt.test.utils.KotlinCoreEnvironmentWrapper
import io.github.detekt.test.utils.createEnvironment
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class IgnoredReturnValueRuleTest {

private val wrapper = createEnvironment()
private val env = wrapper.env


@Test
fun `reports ignored return values`() {
val code = """
fun returnsString(): String = "Hello"
fun returnsInt(): Int = 42
fun returnsNothing(): Nothing = throw Exception("Nothing")
fun returnsUnit(): Unit {}
fun test() {
returnsString()
returnsInt()
returnsNothing()
returnsUnit()
val x = returnsString()
if (returnsInt() > 0) {}
}
""".trimIndent()

val findings = IgnoredReturnValueRule(Config.empty).compileAndLintWithContext(env, code)

println("Number of findings: ${findings.size}")
findings.forEachIndexed { index, finding ->
println("Finding $index:")
println(" Message: ${finding.message}")
println(" Location: ${finding.entity.location}")
println(" Signature: ${finding.entity.signature}")
}

assertThat(findings).hasSize(2)
assertThat(findings[0].message).isEqualTo("The return value of this function call is ignored.")
assertThat(findings[1].message).isEqualTo("The return value of this function call is ignored.")
}

// Update other test methods similarly...

@Test
fun `does not report when return value is explicitly ignored with underscore`() {
val code = """
fun returnsString(): String = "Hello"
fun returnsInt(): Int = 42
fun returnsNothing(): Nothing = throw Exception("Nothing")
fun returnsUnit(): Unit {}
fun test() {
returnsString()
returnsInt()
returnsNothing()
returnsUnit()
val x = returnsString()
if (returnsInt() > 0) {}
_ = returnsString()
_ = returnsInt()
}
""".trimIndent()

val findings = IgnoredReturnValueRule(Config.empty).compileAndLint(code)
val findings = IgnoredReturnValueRule(Config.empty).compileAndLintWithContext(wrapper.env, code)

assertThat(findings).hasSize(2)
assertThat(findings[0].message).isEqualTo("The return value of this function call is ignored.")
assertThat(findings[1].message).isEqualTo("The return value of this function call is ignored.")
assertThat(findings).isEmpty()
}

@Test
Expand All @@ -45,7 +78,7 @@ class IgnoredReturnValueRuleTest {
}
""".trimIndent()

val findings = IgnoredReturnValueRule(Config.empty).compileAndLint(code)
val findings = IgnoredReturnValueRule(Config.empty).compileAndLintWithContext(wrapper.env, code)

assertThat(findings).isEmpty()
}
Expand All @@ -62,7 +95,7 @@ class IgnoredReturnValueRuleTest {
}
""".trimIndent()

val findings = IgnoredReturnValueRule(Config.empty).compileAndLint(code)
val findings = IgnoredReturnValueRule(Config.empty).compileAndLintWithContext(wrapper.env, code)

assertThat(findings).isEmpty()
}
Expand Down

0 comments on commit c5bb9b2

Please sign in to comment.