diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseOrEmpty.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseOrEmpty.kt index d37994229a6..10987657ba3 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseOrEmpty.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseOrEmpty.kt @@ -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 @@ -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 diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseOrEmptySpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseOrEmptySpec.kt index b4a53be9904..ea9b2900b85 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseOrEmptySpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UseOrEmptySpec.kt @@ -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? = null + } + fun test(c: C) { + c["key"] ?: emptyList() + } + """ + val findings = subject.compileAndLintWithContext(env, code) + assertThat(findings).hasSize(1) + } } @Nested @@ -178,6 +192,9 @@ class UseOrEmptySpec(val env: KotlinCoreEnvironment) { fun test(x: List?) { val a = x ?: emptySet() } + fun test(c: Any?) { + val x = c ?: emptyList() + } """ val findings = subject.compileAndLintWithContext(env, code) assertThat(findings).isEmpty() @@ -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 get(key: String): List? = null + } + fun test(c: C) { + val x = c["key"] ?: emptyList() + val y: List = c["key"] ?: emptyList() + val z = (c["key"]) ?: emptyList() + } + """ + val findings = subject.compileAndLintWithContext(env, code) + assertThat(findings).isEmpty() + } } }