Skip to content

Commit

Permalink
Implement MultilineRawString
Browse files Browse the repository at this point in the history
  • Loading branch information
BraisGabin committed Jul 8, 2022
1 parent 8940fb6 commit f16d3cc
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 0 deletions.
2 changes: 2 additions & 0 deletions detekt-core/src/main/resources/default-detekt-config.yml
Expand Up @@ -597,6 +597,8 @@ style:
active: true
MultilineLambdaItParameter:
active: false
MultilineRawString:
active: false
NestedClassesVisibility:
active: true
NewLineAtEndOfFile:
Expand Down
@@ -0,0 +1,74 @@
package io.gitlab.arturbosch.detekt.rules.style

import io.gitlab.arturbosch.detekt.api.CodeSmell
import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.api.Debt
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 org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis

/**
* All the Raw strings that have more than one line should be followed by `trimMargin()` or `trimIndent()`.
*
* <noncompliant>
* """
* Hello World!
* How are you?
* """
* </noncompliant>
*
* <compliant>
* """
* | Hello World!
* | How are you?
* """.trimMargin()
*
* """
* Hello World!
* How are you?
* """.trimIndent()
*
* """Hello World! How are you?"""
* </compliant>
*/
class MultilineRawString(val config: Config) : Rule(config) {
override val issue = Issue(
javaClass.simpleName,
Severity.Style,
"Multiline raw strings should be followed by `trimMargin()` or `trimIndent()`.",
Debt.FIVE_MINS
)

override fun visitStringTemplateExpression(expression: KtStringTemplateExpression) {
super.visitStringTemplateExpression(expression)

if (expression.text.lines().count() == 1) return

val nextCall = expression.getQualifiedExpressionForSelectorOrThis()
.getQualifiedExpressionForReceiver()
?.selectorExpression
?.asKtCallExpression()
?.calleeExpression
?.text

if (nextCall !in trimFunctions) {
report(
CodeSmell(
issue,
Entity.from(expression),
"Multiline raw strings should be followd by `trimMargin()` or `trimIndent()`",
)
)
}
}
}

private fun KtExpression.asKtCallExpression(): KtCallExpression? = this as? KtCallExpression

private val trimFunctions = listOf("trimIndent", "trimMargin")
Expand Up @@ -95,6 +95,7 @@ class StyleGuideProvider : DefaultRuleSetProvider {
RedundantHigherOrderMapUsage(config),
UseIfEmptyOrIfBlank(config),
MultilineLambdaItParameter(config),
MultilineRawString(config),
UseIsNullOrEmpty(config),
UseOrEmpty(config),
UseAnyOrNoneInsteadOfFind(config),
Expand Down
@@ -0,0 +1,87 @@
@file:Suppress("StringTemplate")

package io.gitlab.arturbosch.detekt.rules.style

import io.gitlab.arturbosch.detekt.api.Config
import io.gitlab.arturbosch.detekt.test.compileAndLint
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class MultilineRawStringSpec {
val subject = MultilineRawString(Config.empty)

@Test
fun `raises multiline raw strings without trim`() {
val code = """
val a = ${TQ}
Hello world!
${TQ}
"""
subject.compileAndLint(code)
assertThat(subject.findings).hasSize(1)
}

@Test
fun `raises multiline raw strings with lenght`() {
val code = """
val a = ${TQ}
Hello world!
${TQ}.length
"""
subject.compileAndLint(code)
assertThat(subject.findings).hasSize(1)
}

@Test
fun `doesn't raise multiline raw strings without trimIndent`() {
val code = """
val a = ${TQ}
Hello world!
${TQ}.trimIndent()
"""
subject.compileAndLint(code)
assertThat(subject.findings).isEmpty()
}

@Test
fun `doesn't raise multiline raw strings without trimMargin`() {
val code = """
val a = ${TQ}
|Hello world!
${TQ}.trimMargin()
"""
subject.compileAndLint(code)
assertThat(subject.findings).isEmpty()
}

@Test
fun `doesn't raise multiline raw strings without trimMargin with parameter`() {
val code = """
val a = ${TQ}
>Hello world!
${TQ}.trimMargin(">")
"""
subject.compileAndLint(code)
assertThat(subject.findings).isEmpty()
}

@Test
fun `don't raise one line raw strings`() {
val code = """
val a = ${TQ}Hello world!${TQ}
"""
subject.compileAndLint(code)
assertThat(subject.findings).isEmpty()
}

@Test
fun `doesn't raise if it is not a raw string`() {
val code = """
val a = "Hello world!"
"""
subject.compileAndLint(code)
assertThat(subject.findings).isEmpty()
}
}

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

0 comments on commit f16d3cc

Please sign in to comment.