Skip to content

Commit

Permalink
UseOrEmpty: fix false positive for indexing operator calls with type …
Browse files Browse the repository at this point in the history
…parameters (#4804)

* UseOrEmpty: fix false positive for indexing operator calls with type parameters

* Fix test
  • Loading branch information
t-kameyama committed May 5, 2022
1 parent 61cfc24 commit da0c924
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 0 deletions.
Expand Up @@ -8,12 +8,15 @@ import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.internal.RequiresTypeResolution
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.psi2ir.deparenthesize
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.getType
Expand Down Expand Up @@ -61,6 +64,14 @@ class UseOrEmpty(config: Config = Config.empty) : Rule(config) {

val leftType = left.getType(bindingContext) ?: return
if (!leftType.isNullable()) return
if (left.deparenthesize() is KtArrayAccessExpression) {
val functionDescriptor = left.getResolvedCall(bindingContext)?.resultingDescriptor as? FunctionDescriptor
if (functionDescriptor != null &&
functionDescriptor.isOperator &&
functionDescriptor.typeParameters.isNotEmpty()
) return
}

val rightType = right.getType(bindingContext) ?: return
if (!leftType.makeNotNullable().isSubtypeOf(rightType)) return

Expand Down
Expand Up @@ -146,6 +146,20 @@ class UseOrEmptySpec(val env: KotlinCoreEnvironment) {
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
}

@Test
fun `indexing operator call`() {
val code = """
class C {
operator fun get(key: String): List<Int>? = null
}
fun test(c: C) {
c["key"] ?: emptyList()
}
"""
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).hasSize(1)
}
}

@Nested
Expand Down Expand Up @@ -178,6 +192,9 @@ class UseOrEmptySpec(val env: KotlinCoreEnvironment) {
fun test(x: List<Int>?) {
val a = x ?: emptySet()
}
fun test(c: Any?) {
val x = c ?: emptyList<Int>()
}
"""
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).isEmpty()
Expand All @@ -204,5 +221,21 @@ class UseOrEmptySpec(val env: KotlinCoreEnvironment) {
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).isEmpty()
}

@Test
fun `indexing operator call with type parameter`() {
val code = """
class C {
operator fun <T> get(key: String): List<T>? = null
}
fun test(c: C) {
val x = c["key"] ?: emptyList<Int>()
val y: List<Int> = c["key"] ?: emptyList()
val z = (c["key"]) ?: emptyList<Int>()
}
"""
val findings = subject.compileAndLintWithContext(env, code)
assertThat(findings).isEmpty()
}
}
}

0 comments on commit da0c924

Please sign in to comment.