Skip to content

Commit

Permalink
UseArrayLiteralsInAnnotations: fix false negative with primitive arra…
Browse files Browse the repository at this point in the history
…y factory calls (#5482)
  • Loading branch information
t-kameyama committed Oct 26, 2022
1 parent 94a1d9f commit ecbc7c6
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 3 deletions.
Expand Up @@ -8,7 +8,12 @@ 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.ActiveByDefault
import io.gitlab.arturbosch.detekt.rules.safeAs
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
import org.jetbrains.kotlin.psi.psiUtil.containingClass

/**
* This rule detects annotations which use the arrayOf(...) syntax instead of the array literal [...] syntax.
Expand All @@ -34,14 +39,39 @@ class UseArrayLiteralsInAnnotations(config: Config = Config.empty) : Rule(config

override fun visitAnnotationEntry(annotationEntry: KtAnnotationEntry) {
for (argument in annotationEntry.valueArguments) {
val expr = argument.getArgumentExpression()?.text ?: continue
if (expr.startsWith(LONG_ANNOTATION_LITERAL_FORM)) {
if (argument.getArgumentExpression().isArrayOfFunctionCall()) {
report(CodeSmell(issue, Entity.from(argument.asElement()), issue.description))
}
}
}

override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor) {
if (constructor.containingClass()?.isAnnotation() != true) return
for (parameter in constructor.valueParameters) {
val defaultValue = parameter.defaultValue ?: continue
if (defaultValue.isArrayOfFunctionCall()) {
report(CodeSmell(issue, Entity.from(defaultValue), issue.description))
}
}
}

private fun KtExpression?.isArrayOfFunctionCall(): Boolean =
this?.safeAs<KtCallExpression>()?.calleeExpression?.text in arrayOfFunctions

companion object {
const val LONG_ANNOTATION_LITERAL_FORM = "arrayOf("
private val arrayOfFunctions = listOf(
"boolean",
"char",
"byte",
"short",
"int",
"long",
"float",
"double",
"ubyte",
"ushort",
"uint",
"ulong",
).map { "${it}ArrayOf" }.toSet() + "arrayOf"
}
}
Expand Up @@ -22,6 +22,41 @@ class UseArrayLiteralsInAnnotationsSpec {
assertThat(findings).hasSize(1)
}

@Test
fun `finds intArrayOf usage`() {
val code = """
annotation class Test(val values: IntArray)
@Test(intArrayOf(1, 2))
fun test() {}
""".trimIndent()
subject.compileAndLint(code)
assertThat(subject.findings).hasSize(1)
}

@Test
fun `finds longArrayOf usage`() {
val code = """
annotation class Test(val values: LongArray)
@Test(longArrayOf(1, 2))
fun test() {}
""".trimIndent()
subject.compileAndLint(code)
assertThat(subject.findings).hasSize(1)
}

@Test
fun `finds arrayOf usage as default value`() {
val code = """
annotation class Test(val s: Array<String> = arrayOf("a", "b"))
""".trimIndent()
subject.compileAndLint(code)
assertThat(subject.findings)
.hasSize(1)
.hasTextLocations(45 to 62)
}

@Test
@DisplayName("expects [] syntax")
fun expectsBracketSyntax() {
Expand Down

0 comments on commit ecbc7c6

Please sign in to comment.