Skip to content

Commit

Permalink
TrimMultilineRawString: fix false positive when it's expected as cons…
Browse files Browse the repository at this point in the history
…tant
  • Loading branch information
t-kameyama committed Oct 26, 2022
1 parent 3f5489c commit cf86b61
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 1 deletion.
Expand Up @@ -7,11 +7,20 @@ import io.gitlab.arturbosch.detekt.api.Entity
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.rules.isConstant
import io.gitlab.arturbosch.detekt.rules.safeAs
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType

/**
* All the Raw strings that have more than one line should be followed by `trimMargin()` or `trimIndent()`.
Expand Down Expand Up @@ -48,7 +57,7 @@ class TrimMultilineRawString(val config: Config) : Rule(config) {
override fun visitStringTemplateExpression(expression: KtStringTemplateExpression) {
super.visitStringTemplateExpression(expression)

if (expression.isRawStringWithLineBreak() && !expression.isTrimmed()) {
if (expression.isRawStringWithLineBreak() && !expression.isTrimmed() && !expression.isExpectedAsConstant()) {
report(
CodeSmell(
issue,
Expand Down Expand Up @@ -76,4 +85,23 @@ fun KtStringTemplateExpression.isTrimmed(): Boolean {
return nextCall in trimFunctions
}

@Suppress("ReturnCount")
private fun KtStringTemplateExpression.isExpectedAsConstant(): Boolean {
val expression = KtPsiUtil.safeDeparenthesize(this)

val property = getStrictParentOfType<KtProperty>()?.takeIf { it.initializer == expression }
if (property != null && property.isConstant()) return true

val argument = expression.getStrictParentOfType<KtValueArgument>()
?.takeIf { it.getArgumentExpression() == expression }
if (argument?.parent?.parent is KtAnnotationEntry) return true

val parameter = expression.getStrictParentOfType<KtParameter>()
?.takeIf { it.defaultValue == expression }
val primaryConstructor = parameter?.parent?.parent?.safeAs<KtPrimaryConstructor>()
if (primaryConstructor?.containingClass()?.isAnnotation() == true) return true

return false
}

private val trimFunctions = listOf("trimIndent", "trimMargin")
Expand Up @@ -93,6 +93,86 @@ class TrimMultilineRawStringSpec {
subject.compileAndLint(code)
assertThat(subject.findings).isEmpty()
}

@Test
fun `doesn't raise on constant`() {
val code = """
const val s =
${TQ}
Given something
When something
Then something
${TQ}
""".trimIndent()
subject.compileAndLint(code)
assertThat(subject.findings).isEmpty()
}

@Test
fun `doesn't raise on annotation entry arguments`() {
val code = """
annotation class DisplayName(val s: String)
@DisplayName(
${TQ}
Given something
When something
Then something
${TQ}
)
class Foo
""".trimIndent()
subject.compileAndLint(code)
assertThat(subject.findings).isEmpty()
}

@Test
fun `doesn't raise on annotation constructor parameters`() {
val code = """
annotation class DisplayName(
val s: String =
${TQ}
Given something
When something
Then something
${TQ}
)
""".trimIndent()
subject.compileAndLint(code)
assertThat(subject.findings).isEmpty()
}

@Test
fun `raises on function arguments`() {
val code = """
fun foo(s: String) {}
val bar = foo(
${TQ}
Given something
When something
Then something
${TQ}
)
class Foo
""".trimIndent()
subject.compileAndLint(code)
assertThat(subject.findings).hasSize(1)
}

@Test
fun `raises on class constructor parameters`() {
val code = """
class Foo(
val s: String =
${TQ}
Given something
When something
Then something
${TQ}
)
""".trimIndent()
subject.compileAndLint(code)
assertThat(subject.findings).hasSize(1)
}
}

private const val TQ = "\"\"\""

0 comments on commit cf86b61

Please sign in to comment.