diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StringShouldBeRawString.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StringShouldBeRawString.kt index 44829ab2df0..417bacc9116 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StringShouldBeRawString.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/StringShouldBeRawString.kt @@ -11,10 +11,12 @@ import io.gitlab.arturbosch.detekt.api.config import io.gitlab.arturbosch.detekt.api.internal.Configuration import org.jetbrains.kotlin.com.intellij.psi.PsiElement import org.jetbrains.kotlin.psi.KtBinaryExpression +import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtParenthesizedExpression import org.jetbrains.kotlin.psi.KtStringTemplateExpression +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate import org.jetbrains.kotlin.psi2ir.deparenthesize @@ -81,6 +83,17 @@ class StringShouldBeRawString(config: Config) : Rule(config) { override fun visitStringTemplateExpression(expression: KtStringTemplateExpression) { super.visitStringTemplateExpression(expression) + + val callExpression = expression.getParentOfType(strict = true) + + if (callExpression?.calleeExpression?.text in listOfAllowedMethod) { + return + } + + if (expression.text.matches(regexForOnlyQuotes)) { + return + } + val expressionParent = expression.getParentExpressionAfterParenthesis() val rootElement = expression.getRootExpression() if ( @@ -153,5 +166,10 @@ class StringShouldBeRawString(config: Config) : Rule(config) { companion object { private val REGEX_FOR_ESCAPE_CHARS = """\\[t"\\n]""".toRegex() + private val listOfAllowedMethod = listOf( + "replaceIndent", + "prependIndent", + ) + private val regexForOnlyQuotes = """"(?:\\")*"""".toRegex() } } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/StringShouldBeRawStringSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/StringShouldBeRawStringSpec.kt index 6dd2930fa1e..ee30d69ac0e 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/StringShouldBeRawStringSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/StringShouldBeRawStringSpec.kt @@ -366,6 +366,70 @@ class StringShouldBeRawStringSpec { assertThat(subject.findings).isEmpty() } + @Test + fun `does not report when replaceIndent is used - #6145`() { + val code = """ + fun test() { + val x = ""${'"'} + ... + ""${'"'}.replaceIndent("\t\t\t\t") + } + """.trimIndent() + val subject = StringShouldBeRawString(TestConfig(MAX_ESCAPED_CHARACTER_COUNT to 0)) + subject.compileAndLint(code) + assertThat(subject.findings).isEmpty() + } + + @Test + fun `does not report when replaceIndent is used but report other expression inside it`() { + val code = """ + fun test() { + val x = ""${'"'} + ... + ""${'"'}.replaceIndent("\t\t\t\t".also { + "\t a \n" + } + ) + } + """.trimIndent() + val subject = StringShouldBeRawString(TestConfig(MAX_ESCAPED_CHARACTER_COUNT to 0)) + subject.compileAndLint(code) + assertThat(subject.findings).hasSize(1) + assertThat(subject.findings[0]).hasSourceLocation(5, 13) + } + + @Test + fun `does not report when prependIndent is used - #6145`() { + val code = """ + fun test() { + val x = ""${'"'} + ... + ""${'"'}.trimIndent() + val usage = ""${'"'} + ... + ${'$'}{x.prependIndent("\t\t\t\t")} + ""${'"'} + } + """.trimIndent() + val subject = StringShouldBeRawString(TestConfig(MAX_ESCAPED_CHARACTER_COUNT to 0)) + subject.compileAndLint(code) + assertThat(subject.findings).isEmpty() + } + + @Test + fun `does not report when only quotes is present - #6145`() { + val code = """ + fun test() { + val triplet = "\"\"\"" + val sextuple = "\"\"\"\"\"\"" + val quadrupleQuintuplePair = "\"\"\"\"" + "\"\"\"\"\"" + } + """.trimIndent() + val subject = StringShouldBeRawString(TestConfig(MAX_ESCAPED_CHARACTER_COUNT to 0)) + subject.compileAndLint(code) + assertThat(subject.findings).isEmpty() + } + companion object { private const val MAX_ESCAPED_CHARACTER_COUNT = "maxEscapedCharacterCount" private const val IGNORED_CHARACTERS = "ignoredCharacters"