From 6101bf71d5666a51671991043dac9b8edd160069 Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Thu, 31 Oct 2024 12:32:35 +0200 Subject: [PATCH] [Kotlin] Add inspection for multi-dollar string conversion KTIJ-31819 (cherry picked from commit 7908c8ca0d47cf954c8af01d40bc9a323904395a) KT-CR-18891 GitOrigin-RevId: cd551d652a3784abba99e1e68eb12e0be82212a1 --- .../messages/KotlinBundle.properties | 3 + .../CanConvertToMultiDollarString.html | 23 +++ .../impl/base/PrefixedStringTemplateUtils.kt | 94 ++++++------ .../kotlin.code-insight.inspections.k2.xml | 8 ++ ...CanConvertToMultiDollarStringInspection.kt | 72 ++++++++++ .../CanUnescapeDollarLiteralInspection.kt | 1 - .../tests/K2LocalInspectionTestGenerated.java | 134 ++++++++++++++++++ .../ConvertToMultiDollarStringIntention.kt | 19 +-- .../.k2Inspection | 1 + .../blockDollarWithChar.kt | 5 + .../blockDollarWithChar.kt.after | 5 + .../blockDollarWithString.kt | 5 + .../blockDollarWithString.kt.after | 5 + .../escapedDollar.kt | 5 + .../escapedDollar.kt.after | 5 + ...escapedDollarCaretInsideBlockExpression.kt | 5 + ...dDollarCaretInsideBlockExpression.kt.after | 5 + .../multipleSimplifiableDollars.kt | 5 + .../multipleSimplifiableDollars.kt.after | 5 + .../notReplaceableAlreadyPrefixed.kt | 6 + .../notReplaceableFirstIsTooLong.kt | 6 + .../notReplaceableNothingToChange.kt | 6 + ...tReplaceableNothingToChangeTripleQuoted.kt | 6 + ...bleNothingToChangeTripleQuotedMultiline.kt | 9 ++ .../notReplaceableSecondIsTooLong.kt | 6 + .../notReplaceableTooManyDollars.kt | 6 + ...aceableTooManyDollarsBacktickIdentifier.kt | 6 + .../notReplaceableTooManyDollarsBlock.kt | 6 + ...eableTooManyDollarsUnderscoreIdentifier.kt | 6 + .../notReplaceableTripleQuotedMultiLine.kt | 10 ++ .../notReplaceableTripleQuotedPrefixed.kt | 6 + .../notReplaceableTripleQuotedSingleLine.kt | 6 + .../replaceableManyDollars.kt | 5 + .../replaceableManyDollars.kt.after | 5 + .../replaceableManyDollarsTwoSeqences.kt | 5 + ...replaceableManyDollarsTwoSeqences.kt.after | 5 + .../tripleQuotedReplaceableMultiLine.kt | 9 ++ .../tripleQuotedReplaceableMultiLine.kt.after | 9 ++ .../tripleQuotedReplaceableMultiLineLong.kt | 9 ++ ...pleQuotedReplaceableMultiLineLong.kt.after | 9 ++ .../tripleQuotedReplaceableSingleLine.kt | 5 + ...tripleQuotedReplaceableSingleLine.kt.after | 5 + .../tripleQuotedReplaceableSingleLineLong.kt | 5 + ...leQuotedReplaceableSingleLineLong.kt.after | 5 + .../fe10/testGenerator/Fe10GenerateTests.kt | 1 + .../codeinsight/GenerateK2InspectionTests.kt | 1 + 46 files changed, 512 insertions(+), 56 deletions(-) create mode 100644 plugins/kotlin/code-insight/descriptions/resources-en/inspectionDescriptions/CanConvertToMultiDollarString.html create mode 100644 plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/CanConvertToMultiDollarStringInspection.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/.k2Inspection create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableAlreadyPrefixed.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableFirstIsTooLong.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChange.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuoted.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuotedMultiline.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableSecondIsTooLong.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollars.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBacktickIdentifier.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBlock.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsUnderscoreIdentifier.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedMultiLine.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedPrefixed.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedSingleLine.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt.after create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt create mode 100644 plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt.after diff --git a/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties b/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties index 26caf932bde8..abac046aeaab 100644 --- a/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties +++ b/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties @@ -2288,6 +2288,9 @@ inspection.can.be.primary.constructor.property.display.name=Property is explicit inspection.can.unescape.dollar.literal.inspection.display.name=Escaped dollar characters in string can be simplified inspection.can.unescape.dollar.literal.inspection.problem.description=Escaped dollar characters in the string can be simplified replace.with.dollar.literals=Replace escaped dollars with literals +inspection.can.convert.to.multi.dollar.string.display.name=Interpolation prefix can simplify string +inspection.can.convert.to.multi.dollar.string.problem.description=An interpolation prefix can simplify the string +add.interpolation.prefix=Add interpolation prefix inspection.has.platform.type.display.name=Function or property has platform type inspection.leaking.this.display.name=Leaking 'this' in constructor inspection.redundant.if.display.name=Redundant 'if' statement diff --git a/plugins/kotlin/code-insight/descriptions/resources-en/inspectionDescriptions/CanConvertToMultiDollarString.html b/plugins/kotlin/code-insight/descriptions/resources-en/inspectionDescriptions/CanConvertToMultiDollarString.html new file mode 100644 index 000000000000..3e6a3b2a7485 --- /dev/null +++ b/plugins/kotlin/code-insight/descriptions/resources-en/inspectionDescriptions/CanConvertToMultiDollarString.html @@ -0,0 +1,23 @@ + + +Reports that a string can benefit from a conversion to a version with an interpolation prefix. +

+An interpolation prefix may make a string easier to comprehend, since prefixed strings can use dollar literals without additional escaping. +

+

Example:

+

+val jsonSchema: String = """
+{
+  "${'$'}id": "https://example.com/product.schema.json",
+}"""
+
+The quick fix converts all safely replaceable escaped dollar characters to regular dollar characters: +

+val jsonSchema: String = $$"""
+{
+  "$id": "https://example.com/product.schema.json",
+}"""
+
+ + + \ No newline at end of file diff --git a/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/PrefixedStringTemplateUtils.kt b/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/PrefixedStringTemplateUtils.kt index 83781086d763..8ffd831cef30 100644 --- a/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/PrefixedStringTemplateUtils.kt +++ b/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/PrefixedStringTemplateUtils.kt @@ -3,22 +3,13 @@ package org.jetbrains.kotlin.idea.codeinsights.impl.base import com.intellij.openapi.util.text.StringUtil -import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry -import org.jetbrains.kotlin.psi.KtEscapeStringTemplateEntry -import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.KtSimpleNameStringTemplateEntry -import org.jetbrains.kotlin.psi.KtStringTemplateEntry -import org.jetbrains.kotlin.psi.KtStringTemplateEntryWithExpression -import org.jetbrains.kotlin.psi.KtStringTemplateExpression +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isSingleQuoted import org.jetbrains.kotlin.psi.psiUtil.plainContent private const val DEFAULT_INTERPOLATION_PREFIX_LENGTH: Int = 2 private const val INTERPOLATION_PREFIX_LENGTH_THRESHOLD: Int = 5 -private const val DOLLAR: String = "$" - val dollarLiteralExpressions: Array = arrayOf( "'$'", "\"$\"" ) @@ -103,11 +94,11 @@ class EntryUpdateDiff( } } -internal fun KtStringTemplateEntry.asOneToOneDiff(newText: String): EntryUpdateDiff { +private fun KtStringTemplateEntry.asOneToOneDiff(newText: String): EntryUpdateDiff { return EntryUpdateDiff(0.. 0) { "Unexpected string template prefix length: $prefixLength" } val replacement = when (this) { @@ -119,7 +110,7 @@ fun KtStringTemplateEntryWithExpression.changePrefixLength(prefixLength: Int): K return replacement } -fun KtBlockStringTemplateEntry.changePrefixLength(prefixLength: Int): KtStringTemplateEntryWithExpression { +private fun KtBlockStringTemplateEntry.changePrefixLength(prefixLength: Int): KtStringTemplateEntryWithExpression { require(prefixLength > 0) { "Unexpected string template entry prefix length: $prefixLength" } val ktPsiFactory = KtPsiFactory(project) val expression = this.expression @@ -138,7 +129,7 @@ fun KtBlockStringTemplateEntry.changePrefixLength(prefixLength: Int): KtStringTe return replacement } -fun KtSimpleNameStringTemplateEntry.changePrefixLength(prefixLength: Int): KtSimpleNameStringTemplateEntry { +private fun KtSimpleNameStringTemplateEntry.changePrefixLength(prefixLength: Int): KtSimpleNameStringTemplateEntry { require(prefixLength > 0) { "Unexpected string template entry prefix length: $prefixLength" } val ktPsiFactory = KtPsiFactory(project) return ktPsiFactory.createMultiDollarSimpleNameStringTemplateEntry( @@ -147,7 +138,7 @@ fun KtSimpleNameStringTemplateEntry.changePrefixLength(prefixLength: Int): KtSim ) } -fun KtLiteralStringTemplateEntry.escapeIfNecessary( +private fun KtLiteralStringTemplateEntry.escapeIfNecessary( newPrefixLength: Int, isSourceSingleQuoted: Boolean, isDestinationSingleQuoted: Boolean, @@ -183,7 +174,7 @@ private fun KtLiteralStringTemplateEntry.escapeDollarIfNecessary( ) } -fun KtLiteralStringTemplateEntry.escapeSpecialCharacters(): List { +private fun KtLiteralStringTemplateEntry.escapeSpecialCharacters(): List { val escaper = StringUtil.escaper(true, "\"") var from = 0 var to = 0 @@ -216,7 +207,7 @@ fun KtLiteralStringTemplateEntry.escapeSpecialCharacters(): List { - return unescapedValue == "$" - } - - is KtBlockStringTemplateEntry -> { - expression?.text in dollarLiteralExpressions - } - - else -> false - } +fun KtStringTemplateEntry.isEscapedDollar(): Boolean = when (this) { + is KtEscapeStringTemplateEntry -> this.isEscapedDollar() + is KtBlockStringTemplateEntry -> this.isInterpolatedDollarLiteralExpression() + else -> false } /** - * Convert a string to a multi-dollar string, choosing an appropriate prefix length based on the string's content. - * The function searches for the shortest possible prefix that doesn't exceed [INTERPOLATION_PREFIX_LENGTH_THRESHOLD]. - * If no such prefix exists, the [DEFAULT_INTERPOLATION_PREFIX_LENGTH] is used. + * Context for the multi-dollar conversion inspection and intention. */ -internal fun convertToMultiDollarString(element: KtStringTemplateExpression): KtStringTemplateExpression { +class MultiDollarConversionInfo( + val prefixLength: Int, +) + +fun prepareMultiDollarConversionInfo(element: KtStringTemplateExpression, useFallbackPrefix: Boolean): MultiDollarConversionInfo? { + val suitablePrefixLength = findSuitablePrefixLength(element, useFallbackPrefix) ?: return null + return MultiDollarConversionInfo(suitablePrefixLength) +} + +/** + * Search for the shortest possible prefix that doesn't exceed [INTERPOLATION_PREFIX_LENGTH_THRESHOLD]. + * If no such prefix exists, the [DEFAULT_INTERPOLATION_PREFIX_LENGTH] if [useFallbackPrefix] is `true`, or `null` otherwise. + */ +private fun findSuitablePrefixLength(element: KtStringTemplateExpression, useFallbackPrefix: Boolean): Int? { + val longestUnsafeDollarSequence = longestUnsafeDollarSequenceLength(element, INTERPOLATION_PREFIX_LENGTH_THRESHOLD) + if (longestUnsafeDollarSequence >= INTERPOLATION_PREFIX_LENGTH_THRESHOLD) { + return if (useFallbackPrefix) DEFAULT_INTERPOLATION_PREFIX_LENGTH else null + } + return maxOf(longestUnsafeDollarSequence + 1, DEFAULT_INTERPOLATION_PREFIX_LENGTH) +} + +/** + * Convert a plain string to a multi-dollar string with the specified prefix length + */ +fun convertToMultiDollarString(element: KtStringTemplateExpression, contextInfo: MultiDollarConversionInfo): KtStringTemplateExpression { require(element.interpolationPrefix == null) { "Can't convert the string which already has a prefix to multi-dollar string" } - - val longestUnsafeDollarSequence = longestUnsafeDollarSequenceLength(element, threshold = INTERPOLATION_PREFIX_LENGTH_THRESHOLD) - val prefixLength = if (longestUnsafeDollarSequence in DEFAULT_INTERPOLATION_PREFIX_LENGTH..< INTERPOLATION_PREFIX_LENGTH_THRESHOLD) - longestUnsafeDollarSequence + 1 else DEFAULT_INTERPOLATION_PREFIX_LENGTH - - replaceExpressionEntries(element, prefixLength) + replaceExpressionEntries(element, contextInfo.prefixLength) val replaced = element.replace( KtPsiFactory(element.project).createMultiDollarStringTemplate( content = element.plainContent, - prefixLength = prefixLength, + prefixLength = contextInfo.prefixLength, forceMultiQuoted = !element.isSingleQuoted(), ) ) as KtStringTemplateExpression @@ -306,7 +306,7 @@ internal fun convertToMultiDollarString(element: KtStringTemplateExpression): Kt * Replace dollar escape sequences in a string template if it's safe, i.e., if replacement won't turn a literal part into interpolation. * Both `\$` and `${'$'}` sequences are replaced if possible. */ -internal fun simplifyDollarEntries(element: KtStringTemplateExpression): KtStringTemplateExpression { +fun simplifyDollarEntries(element: KtStringTemplateExpression): KtStringTemplateExpression { val ktPsiFactory = KtPsiFactory(element.project) val prefixLength = element.interpolationPrefix?.textLength?.takeIf { it > 1 } ?: return element @@ -314,12 +314,12 @@ internal fun simplifyDollarEntries(element: KtStringTemplateExpression): KtStrin when (entry) { is KtEscapeStringTemplateEntry -> { if (entry.isEscapedDollar() && entry.isSafeToReplaceWithDollar(prefixLength)) - entry.replace(ktPsiFactory.createLiteralStringTemplateEntry(DOLLAR)) + entry.replace(ktPsiFactory.createLiteralStringTemplateEntry("$")) } is KtBlockStringTemplateEntry -> { if (entry.expression?.text in dollarLiteralExpressions && entry.isSafeToReplaceWithDollar(prefixLength)) - entry.replace(ktPsiFactory.createLiteralStringTemplateEntry(DOLLAR)) + entry.replace(ktPsiFactory.createLiteralStringTemplateEntry("$")) } } } @@ -352,7 +352,7 @@ internal fun longestUnsafeDollarSequenceLength( is KtBlockStringTemplateEntry -> { when { - entry.isSimplifiableInterpolatedDollar() -> current++ + entry.isInterpolatedDollarLiteralExpression() -> current++ else -> { current = 0 } @@ -384,7 +384,7 @@ internal fun longestUnsafeDollarSequenceLength( return longest } -private fun KtBlockStringTemplateEntry.isSimplifiableInterpolatedDollar(): Boolean { +private fun KtBlockStringTemplateEntry.isInterpolatedDollarLiteralExpression(): Boolean { return this.expression?.text in dollarLiteralExpressions } @@ -396,7 +396,7 @@ private fun replaceExpressionEntries(stringTemplate: KtStringTemplateExpression, } } -private fun KtEscapeStringTemplateEntry.isEscapedDollar(): Boolean = unescapedValue == DOLLAR +private fun KtEscapeStringTemplateEntry.isEscapedDollar(): Boolean = unescapedValue == "$" /** * It's unsafe to replace with a `$` if the part before the entry ends with a `$`, and the part after can be considered identifier/block. @@ -406,6 +406,6 @@ private fun KtStringTemplateEntry.isSafeToReplaceWithDollar(prefixLength: Int): if (prevSibling !is KtLiteralStringTemplateEntry) return true val nextSiblingStringLiteral = nextSibling as? KtLiteralStringTemplateEntry ?: return true if (!nextSiblingStringLiteral.canBeConsideredIdentifierOrBlock()) return true - val trailingDollarsLength = prevSibling.text.takeLastWhile { it.toString() == DOLLAR }.length + val trailingDollarsLength = prevSibling.text.takeLastWhile { it.toString() == "$" }.length return trailingDollarsLength + 1 < prefixLength } diff --git a/plugins/kotlin/code-insight/inspections-k2/resources/kotlin.code-insight.inspections.k2.xml b/plugins/kotlin/code-insight/inspections-k2/resources/kotlin.code-insight.inspections.k2.xml index 9b5f6594013a..35746406c20f 100644 --- a/plugins/kotlin/code-insight/inspections-k2/resources/kotlin.code-insight.inspections.k2.xml +++ b/plugins/kotlin/code-insight/inspections-k2/resources/kotlin.code-insight.inspections.k2.xml @@ -68,6 +68,14 @@ language="kotlin" key="inspection.can.unescape.dollar.literal.inspection.display.name" bundle="messages.KotlinBundle"/> + + () { + + override fun isApplicableByPsi(element: KtStringTemplateExpression): Boolean { + if (!element.languageVersionSettings.supportsFeature(LanguageFeature.MultiDollarInterpolation)) return false + return element.interpolationPrefix == null + } + + override fun buildVisitor( + holder: ProblemsHolder, + isOnTheFly: Boolean + ): KtVisitor<*, *> { + return object : KtVisitorVoid() { + override fun visitStringTemplateExpression(entry: KtStringTemplateExpression) { + visitTargetElement(entry, holder, isOnTheFly) + } + } + } + + context(KaSession) + override fun prepareContext(element: KtStringTemplateExpression): MultiDollarConversionInfo? { + if (!element.entries.any { it.isEscapedDollar() }) return null + return prepareMultiDollarConversionInfo(element, useFallbackPrefix = false) + } + + override fun getProblemDescription( + element: KtStringTemplateExpression, + context: MultiDollarConversionInfo, + ): @InspectionMessage String { + return KotlinBundle.message("inspection.can.convert.to.multi.dollar.string.problem.description") + } + + override fun createQuickFix( + element: KtStringTemplateExpression, + context: MultiDollarConversionInfo, + ): KotlinModCommandQuickFix { + return object : KotlinModCommandQuickFix() { + override fun getFamilyName(): @IntentionFamilyName String { + return KotlinBundle.message("add.interpolation.prefix") + } + + override fun applyFix( + project: Project, + element: KtStringTemplateExpression, + updater: ModPsiUpdater + ) { + val multiDollarVersion = convertToMultiDollarString(element, context) + simplifyDollarEntries(multiDollarVersion) + } + } + } +} diff --git a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/CanUnescapeDollarLiteralInspection.kt b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/CanUnescapeDollarLiteralInspection.kt index fb1cdabf264b..53e9e43bc58c 100644 --- a/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/CanUnescapeDollarLiteralInspection.kt +++ b/plugins/kotlin/code-insight/inspections-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/inspections/CanUnescapeDollarLiteralInspection.kt @@ -14,7 +14,6 @@ import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinApplicableInspectionBase import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinModCommandQuickFix import org.jetbrains.kotlin.idea.codeinsights.impl.base.canBeStartOfIdentifierOrBlock -import org.jetbrains.kotlin.idea.codeinsights.impl.base.dollarLiteralExpressions import org.jetbrains.kotlin.idea.codeinsights.impl.base.isEscapedDollar import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isSingleQuoted diff --git a/plugins/kotlin/code-insight/inspections-k2/tests/test/org/jetbrains/kotlin/idea/k2/inspections/tests/K2LocalInspectionTestGenerated.java b/plugins/kotlin/code-insight/inspections-k2/tests/test/org/jetbrains/kotlin/idea/k2/inspections/tests/K2LocalInspectionTestGenerated.java index 46012113e308..d40d5e16c10d 100644 --- a/plugins/kotlin/code-insight/inspections-k2/tests/test/org/jetbrains/kotlin/idea/k2/inspections/tests/K2LocalInspectionTestGenerated.java +++ b/plugins/kotlin/code-insight/inspections-k2/tests/test/org/jetbrains/kotlin/idea/k2/inspections/tests/K2LocalInspectionTestGenerated.java @@ -8262,6 +8262,140 @@ public abstract class K2LocalInspectionTestGenerated extends AbstractK2LocalInsp } } + @RunWith(JUnit3RunnerWithInners.class) + @TestMetadata("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString") + public static class CanConvertToMultiDollarString extends AbstractK2LocalInspectionTest { + @java.lang.Override + @org.jetbrains.annotations.NotNull + public final KotlinPluginMode getPluginMode() { + return KotlinPluginMode.K2; + } + + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + @TestMetadata("blockDollarWithChar.kt") + public void testBlockDollarWithChar() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt"); + } + + @TestMetadata("blockDollarWithString.kt") + public void testBlockDollarWithString() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt"); + } + + @TestMetadata("escapedDollar.kt") + public void testEscapedDollar() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt"); + } + + @TestMetadata("escapedDollarCaretInsideBlockExpression.kt") + public void testEscapedDollarCaretInsideBlockExpression() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt"); + } + + @TestMetadata("multipleSimplifiableDollars.kt") + public void testMultipleSimplifiableDollars() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt"); + } + + @TestMetadata("notReplaceableAlreadyPrefixed.kt") + public void testNotReplaceableAlreadyPrefixed() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableAlreadyPrefixed.kt"); + } + + @TestMetadata("notReplaceableFirstIsTooLong.kt") + public void testNotReplaceableFirstIsTooLong() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableFirstIsTooLong.kt"); + } + + @TestMetadata("notReplaceableNothingToChange.kt") + public void testNotReplaceableNothingToChange() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChange.kt"); + } + + @TestMetadata("notReplaceableNothingToChangeTripleQuoted.kt") + public void testNotReplaceableNothingToChangeTripleQuoted() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuoted.kt"); + } + + @TestMetadata("notReplaceableNothingToChangeTripleQuotedMultiline.kt") + public void testNotReplaceableNothingToChangeTripleQuotedMultiline() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuotedMultiline.kt"); + } + + @TestMetadata("notReplaceableSecondIsTooLong.kt") + public void testNotReplaceableSecondIsTooLong() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableSecondIsTooLong.kt"); + } + + @TestMetadata("notReplaceableTooManyDollars.kt") + public void testNotReplaceableTooManyDollars() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollars.kt"); + } + + @TestMetadata("notReplaceableTooManyDollarsBacktickIdentifier.kt") + public void testNotReplaceableTooManyDollarsBacktickIdentifier() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBacktickIdentifier.kt"); + } + + @TestMetadata("notReplaceableTooManyDollarsBlock.kt") + public void testNotReplaceableTooManyDollarsBlock() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBlock.kt"); + } + + @TestMetadata("notReplaceableTooManyDollarsUnderscoreIdentifier.kt") + public void testNotReplaceableTooManyDollarsUnderscoreIdentifier() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsUnderscoreIdentifier.kt"); + } + + @TestMetadata("notReplaceableTripleQuotedMultiLine.kt") + public void testNotReplaceableTripleQuotedMultiLine() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedMultiLine.kt"); + } + + @TestMetadata("notReplaceableTripleQuotedPrefixed.kt") + public void testNotReplaceableTripleQuotedPrefixed() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedPrefixed.kt"); + } + + @TestMetadata("notReplaceableTripleQuotedSingleLine.kt") + public void testNotReplaceableTripleQuotedSingleLine() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedSingleLine.kt"); + } + + @TestMetadata("replaceableManyDollars.kt") + public void testReplaceableManyDollars() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt"); + } + + @TestMetadata("replaceableManyDollarsTwoSeqences.kt") + public void testReplaceableManyDollarsTwoSeqences() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt"); + } + + @TestMetadata("tripleQuotedReplaceableMultiLine.kt") + public void testTripleQuotedReplaceableMultiLine() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt"); + } + + @TestMetadata("tripleQuotedReplaceableMultiLineLong.kt") + public void testTripleQuotedReplaceableMultiLineLong() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt"); + } + + @TestMetadata("tripleQuotedReplaceableSingleLine.kt") + public void testTripleQuotedReplaceableSingleLine() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt"); + } + + @TestMetadata("tripleQuotedReplaceableSingleLineLong.kt") + public void testTripleQuotedReplaceableSingleLineLong() throws Exception { + runTest("../../../idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt"); + } + } + @RunWith(JUnit3RunnerWithInners.class) @TestMetadata("testData/inspectionsLocal") public abstract static class InspectionsLocal extends AbstractK2LocalInspectionTest { diff --git a/plugins/kotlin/code-insight/intentions-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/intentions/ConvertToMultiDollarStringIntention.kt b/plugins/kotlin/code-insight/intentions-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/intentions/ConvertToMultiDollarStringIntention.kt index 656b0aeed9e7..a94072e824a1 100644 --- a/plugins/kotlin/code-insight/intentions-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/intentions/ConvertToMultiDollarStringIntention.kt +++ b/plugins/kotlin/code-insight/intentions-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/intentions/ConvertToMultiDollarStringIntention.kt @@ -10,13 +10,14 @@ import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.codeinsight.api.applicable.intentions.KotlinApplicableModCommandAction -import org.jetbrains.kotlin.idea.k2.codeinsight.intentions.multiDollarStrings.convertToMultiDollarString -import org.jetbrains.kotlin.idea.k2.codeinsight.intentions.multiDollarStrings.simplifyDollarEntries +import org.jetbrains.kotlin.idea.codeinsights.impl.base.MultiDollarConversionInfo +import org.jetbrains.kotlin.idea.codeinsights.impl.base.convertToMultiDollarString +import org.jetbrains.kotlin.idea.codeinsights.impl.base.prepareMultiDollarConversionInfo +import org.jetbrains.kotlin.idea.codeinsights.impl.base.simplifyDollarEntries import org.jetbrains.kotlin.psi.KtStringTemplateExpression -internal class ConvertToMultiDollarStringIntention : KotlinApplicableModCommandAction( - KtStringTemplateExpression::class, -) { +internal class ConvertToMultiDollarStringIntention : + KotlinApplicableModCommandAction(KtStringTemplateExpression::class) { override fun getFamilyName(): @IntentionFamilyName String = KotlinBundle.message("convert.to.multi.dollar.string") override fun isApplicableByPsi(element: KtStringTemplateExpression): Boolean { @@ -27,13 +28,15 @@ internal class ConvertToMultiDollarStringIntention : KotlinApplicableModCommandA override fun invoke( actionContext: ActionContext, element: KtStringTemplateExpression, - elementContext: Unit, + elementContext: MultiDollarConversionInfo, updater: ModPsiUpdater ) { - val replaced = convertToMultiDollarString(element) + val replaced = convertToMultiDollarString(element, elementContext) simplifyDollarEntries(replaced) } context(KaSession) - override fun prepareContext(element: KtStringTemplateExpression): Unit = Unit + override fun prepareContext(element: KtStringTemplateExpression): MultiDollarConversionInfo? { + return prepareMultiDollarConversionInfo(element, useFallbackPrefix = true) + } } diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/.k2Inspection b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/.k2Inspection new file mode 100644 index 000000000000..985d8d9bc6bc --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/.k2Inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.k2.codeinsight.inspections.CanConvertToMultiDollarStringInspection \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt new file mode 100644 index 000000000000..e638e168065b --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + "${'$'}" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt.after new file mode 100644 index 000000000000..60624a2b4b67 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithChar.kt.after @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + $$"$" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt new file mode 100644 index 000000000000..89a8e3de833c --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + "${"$"}" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt.after new file mode 100644 index 000000000000..60624a2b4b67 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/blockDollarWithString.kt.after @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + $$"$" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt new file mode 100644 index 000000000000..297fff59a129 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + "\$" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt.after new file mode 100644 index 000000000000..60624a2b4b67 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollar.kt.after @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + $$"$" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt new file mode 100644 index 000000000000..9f188b36c27b --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + "\$ ${15 + 27}" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt.after new file mode 100644 index 000000000000..31a48768261d --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/escapedDollarCaretInsideBlockExpression.kt.after @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + $$"$ $${15 + 27}" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt new file mode 100644 index 000000000000..2433400361a6 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + "\$\$\$${'$'}${'$'}${"$"}${"$"}" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt.after new file mode 100644 index 000000000000..d9fb8ff2f887 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/multipleSimplifiableDollars.kt.after @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + $$"$$$$$$$" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableAlreadyPrefixed.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableAlreadyPrefixed.kt new file mode 100644 index 000000000000..67e8aaa7f1bb --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableAlreadyPrefixed.kt @@ -0,0 +1,6 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test() { + $$"\$" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableFirstIsTooLong.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableFirstIsTooLong.kt new file mode 100644 index 000000000000..cade25e77457 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableFirstIsTooLong.kt @@ -0,0 +1,6 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test() { + "\$\$\$\$\$Foo \$\$Bar" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChange.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChange.kt new file mode 100644 index 000000000000..5dfb1f9b5763 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChange.kt @@ -0,0 +1,6 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test(some: Int) { + "$10 10$ $$$$$ ${3 + 2} $some" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuoted.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuoted.kt new file mode 100644 index 000000000000..ec7351034606 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuoted.kt @@ -0,0 +1,6 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test(some: Int) { + """$10 10$ $$$$$ ${3 + 2} $some""" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuotedMultiline.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuotedMultiline.kt new file mode 100644 index 000000000000..4d6e289c56a0 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableNothingToChangeTripleQuotedMultiline.kt @@ -0,0 +1,9 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test(some: Int) { + """ + $10 10$ $$$$$ ${3 + 2} $some + $10 10$ $$$$$ ${3 + 2} $some + """.trimIndent() +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableSecondIsTooLong.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableSecondIsTooLong.kt new file mode 100644 index 000000000000..2d4b5792cc63 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableSecondIsTooLong.kt @@ -0,0 +1,6 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test() { + "\$\$Foo \$\$\$\$\$Bar" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollars.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollars.kt new file mode 100644 index 000000000000..c72fb1c03dc1 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollars.kt @@ -0,0 +1,6 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test() { + "\$\$\$\$\$Foo" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBacktickIdentifier.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBacktickIdentifier.kt new file mode 100644 index 000000000000..60fa0278a097 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBacktickIdentifier.kt @@ -0,0 +1,6 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test() { + "\$\$\$\$\$`Foo`" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBlock.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBlock.kt new file mode 100644 index 000000000000..cb4b37195a91 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsBlock.kt @@ -0,0 +1,6 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test() { + "\$\$\$\$\${Foo}" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsUnderscoreIdentifier.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsUnderscoreIdentifier.kt new file mode 100644 index 000000000000..3164f21c0a53 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTooManyDollarsUnderscoreIdentifier.kt @@ -0,0 +1,6 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test() { + "\$\$\$\$\$_Foo" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedMultiLine.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedMultiLine.kt new file mode 100644 index 000000000000..7852ca4e7373 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedMultiLine.kt @@ -0,0 +1,10 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test() { + """ +${'$'}${'$'}${'$'}${'$'}${'$'}Foo +${'$'}${'$'}${'$'}${'$'}${'$'}Bar +${'$'}${'$'}${'$'}${'$'}${'$'}Baz +""" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedPrefixed.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedPrefixed.kt new file mode 100644 index 000000000000..ae1e45cd00a0 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedPrefixed.kt @@ -0,0 +1,6 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test() { + $$"""${'$'}${'$'}${'$'}Foo""" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedSingleLine.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedSingleLine.kt new file mode 100644 index 000000000000..886f4166fe1e --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/notReplaceableTripleQuotedSingleLine.kt @@ -0,0 +1,6 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation +// PROBLEM: none + +fun test() { + """${'$'}${'$'}${'$'}${'$'}${'$'}Foo""" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt new file mode 100644 index 000000000000..5085c1ee98ec --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + "${'$'}\$\$${"$"}Foo" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt.after new file mode 100644 index 000000000000..a5a865b28374 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollars.kt.after @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + $$$$$"$$$$Foo" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt new file mode 100644 index 000000000000..aa35c5d86fc3 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + "${'$'}\$\$${"$"}Foo ${'$'}\$\$${"$"}Bar" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt.after new file mode 100644 index 000000000000..bb4d3b4f5145 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/replaceableManyDollarsTwoSeqences.kt.after @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + $$$$$"$$$$Foo $$$$Bar" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt new file mode 100644 index 000000000000..a2f2fc49ac46 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt @@ -0,0 +1,9 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + """ +\$\$${'$'}${'$'}${'$'}Foo +\$\$${'$'}${'$'}${'$'}Bar +\$\$${'$'}${'$'}${'$'}Baz +""" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt.after new file mode 100644 index 000000000000..2eb8ce58a59f --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLine.kt.after @@ -0,0 +1,9 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + $$$$$""" +\$\$$$$Foo +\$\$$$$Bar +\$\$$$$Baz +""" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt new file mode 100644 index 000000000000..b29d9ad84073 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt @@ -0,0 +1,9 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + """ +${'$'}${'$'}${'$'}${'$'}${'$'}${'$'} +${'$'}${'$'}${'$'}${'$'}${'$'}${'$'} +${'$'}${'$'}${'$'}${'$'}${'$'}${'$'} +""" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt.after new file mode 100644 index 000000000000..db8efceca7bb --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableMultiLineLong.kt.after @@ -0,0 +1,9 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + $$""" +$$$$$$ +$$$$$$ +$$$$$$ +""" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt new file mode 100644 index 000000000000..a9824f8525c1 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + """\$\$${'$'}${'$'}${'$'}Foo""" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt.after new file mode 100644 index 000000000000..ccb040be3136 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLine.kt.after @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + $$$$$"""\$\$$$$Foo""" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt new file mode 100644 index 000000000000..2b082b042c47 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + """${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}""" +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt.after new file mode 100644 index 000000000000..9b579d274e88 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/canConvertToMultiDollarString/tripleQuotedReplaceableSingleLineLong.kt.after @@ -0,0 +1,5 @@ +// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation + +fun test() { + $$"""$$$$$$""" +} \ No newline at end of file diff --git a/plugins/kotlin/util/test-generator-fe10/test/org/jetbrains/kotlin/fe10/testGenerator/Fe10GenerateTests.kt b/plugins/kotlin/util/test-generator-fe10/test/org/jetbrains/kotlin/fe10/testGenerator/Fe10GenerateTests.kt index d968f4498bcb..1950240dd1f4 100644 --- a/plugins/kotlin/util/test-generator-fe10/test/org/jetbrains/kotlin/fe10/testGenerator/Fe10GenerateTests.kt +++ b/plugins/kotlin/util/test-generator-fe10/test/org/jetbrains/kotlin/fe10/testGenerator/Fe10GenerateTests.kt @@ -589,6 +589,7 @@ private fun assembleWorkspace(): TWorkspace = workspace(KotlinPluginMode.K1) { excludedDirectories = listOf( "unusedVariable", // In FE1.0, this is a quickfix rather than a local inspection "canSimplifyDollarLiteral", // K2-only + "canConvertToMultiDollarString", // K2-only ) ) } diff --git a/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2InspectionTests.kt b/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2InspectionTests.kt index 0e9a1f87ab06..eab699ea9678 100644 --- a/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2InspectionTests.kt +++ b/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/codeinsight/GenerateK2InspectionTests.kt @@ -66,6 +66,7 @@ internal fun MutableTWorkspace.generateK2InspectionTests() { model("${idea}/inspectionsLocal/arrayInDataClass") model("${idea}/inspectionsLocal/collections/simplifiableCallChain") model("${idea}/inspectionsLocal/canSimplifyDollarLiteral") + model("${idea}/inspectionsLocal/canConvertToMultiDollarString") model("code-insight/inspections-k2/tests/testData/inspectionsLocal", pattern = pattern) } /**