Skip to content

Commit

Permalink
NonBooleanPropertyPrefixedWithIs: Allow boolean function reference (#…
Browse files Browse the repository at this point in the history
…4684)

* NonBooleanPropertyPrefixedWithIs: Allow boolean function reference properties

* fix detekt

* replace compileAndLintWithContext with lintWithContext
  • Loading branch information
kvn-stgl authored and chao2zhang committed Apr 8, 2022
1 parent 7aaa7ed commit 2cf82e1
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 5 deletions.
Expand Up @@ -10,11 +10,14 @@ import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution
import io.gitlab.arturbosch.detekt.rules.fqNameOrNull
import io.gitlab.arturbosch.detekt.rules.identifierName
import org.jetbrains.kotlin.builtins.isFunctionOrKFunctionTypeWithAnySuspendability
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.typeBinding.createTypeBindingForReturnType
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isBoolean

/**
* Reports when property with 'is' prefix doesn't have a boolean type.
Expand Down Expand Up @@ -63,9 +66,14 @@ class NonBooleanPropertyPrefixedWithIs(config: Config = Config.empty) : Rule(con
val name = declaration.identifierName()

if (name.startsWith("is") && name.length > 2 && !name[2].isLowerCase()) {
val typeName = getTypeName(declaration)
val type = getType(declaration)
val typeName = type?.getTypeName()
val isNotBooleanType = typeName != kotlinBooleanTypeName && typeName != javaBooleanTypeName

if (typeName != null && typeName != kotlinBooleanTypeName && typeName != javaBooleanTypeName) {
if (!typeName.isNullOrEmpty() &&
isNotBooleanType &&
!type.isBooleanFunctionReference()
) {
report(
reportCodeSmell(declaration, name, typeName)
)
Expand All @@ -85,10 +93,17 @@ class NonBooleanPropertyPrefixedWithIs(config: Config = Config.empty) : Rule(con
)
}

private fun getTypeName(parameter: KtCallableDeclaration): String? {
return parameter.createTypeBindingForReturnType(bindingContext)
private fun getType(parameter: KtCallableDeclaration): KotlinType? =
parameter.createTypeBindingForReturnType(bindingContext)
?.type
?.fqNameOrNull()

private fun KotlinType.getTypeName(): String? =
fqNameOrNull()
?.asString()

private fun KotlinType.isBooleanFunctionReference(): Boolean {
if (!isFunctionOrKFunctionTypeWithAnySuspendability) return false

return arguments.count() == 1 && arguments[0].type.isBoolean()
}
}
Expand Up @@ -3,6 +3,7 @@ package io.gitlab.arturbosch.detekt.rules.naming
import io.gitlab.arturbosch.detekt.rules.KotlinCoreEnvironmentTest
import io.gitlab.arturbosch.detekt.test.assertThat
import io.gitlab.arturbosch.detekt.test.compileAndLintWithContext
import io.gitlab.arturbosch.detekt.test.lintWithContext
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -253,5 +254,35 @@ class NonBooleanPropertyWithPrefixIsSpec(val env: KotlinCoreEnvironment) {
assertThat(findings).hasSize(1)
}
}

@Nested
inner class `issue regression test` {
@Test
fun `issue 4674 should handle unknown type as correct`() {
val code = """
class Test {
val isDebuggable get() = BuildConfig.DEBUG
}
"""

// BuildConfig is missing in this test so we can't compile it
val findings = subject.lintWithContext(env, code)

assertThat(findings).isEmpty()
}

@Test
fun `issue 4675 check function reference type parameter`() {
val code = """
val isRemoved = suspend { null == null }
fun trueFun() = true
val isReferenceBoolean = ::trueFun
"""
val findings = subject.compileAndLintWithContext(env, code)

assertThat(findings).isEmpty()
}
}
}
}

0 comments on commit 2cf82e1

Please sign in to comment.