mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[Kotlin] Add inspection for multi-dollar string conversion
KTIJ-31819 (cherry picked from commit 7908c8ca0d47cf954c8af01d40bc9a323904395a) KT-CR-18891 GitOrigin-RevId: cd551d652a3784abba99e1e68eb12e0be82212a1
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7b16461e28
commit
6101bf71d5
@@ -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
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports that a string can benefit from a conversion to a version with an interpolation prefix.
|
||||
<p>
|
||||
An interpolation prefix may make a string easier to comprehend, since prefixed strings can use dollar literals without additional escaping.
|
||||
</p>
|
||||
<p><b>Example:</b></p>
|
||||
<pre><code>
|
||||
val jsonSchema: String = """
|
||||
{
|
||||
"${'$'}id": "https://example.com/product.schema.json",
|
||||
}"""
|
||||
</code></pre>
|
||||
The quick fix converts all safely replaceable escaped dollar characters to regular dollar characters:
|
||||
<pre><code>
|
||||
val jsonSchema: String = $$"""
|
||||
{
|
||||
"$id": "https://example.com/product.schema.json",
|
||||
}"""
|
||||
</code></pre>
|
||||
<!-- tooltip end -->
|
||||
</body>
|
||||
</html>
|
||||
+47
-47
@@ -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<String> = arrayOf(
|
||||
"'$'", "\"$\""
|
||||
)
|
||||
@@ -103,11 +94,11 @@ class EntryUpdateDiff(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KtStringTemplateEntry.asOneToOneDiff(newText: String): EntryUpdateDiff {
|
||||
private fun KtStringTemplateEntry.asOneToOneDiff(newText: String): EntryUpdateDiff {
|
||||
return EntryUpdateDiff(0..<textLength, text, newText)
|
||||
}
|
||||
|
||||
fun KtStringTemplateEntryWithExpression.changePrefixLength(prefixLength: Int): KtStringTemplateEntryWithExpression {
|
||||
private fun KtStringTemplateEntryWithExpression.changePrefixLength(prefixLength: Int): KtStringTemplateEntryWithExpression {
|
||||
require(prefixLength > 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<EntryUpdateDiff> {
|
||||
private fun KtLiteralStringTemplateEntry.escapeSpecialCharacters(): List<EntryUpdateDiff> {
|
||||
val escaper = StringUtil.escaper(true, "\"")
|
||||
var from = 0
|
||||
var to = 0
|
||||
@@ -216,7 +207,7 @@ fun KtLiteralStringTemplateEntry.escapeSpecialCharacters(): List<EntryUpdateDiff
|
||||
return diffs
|
||||
}
|
||||
|
||||
fun KtStringTemplateEntry.unescapeIfPossible(newPrefixLength: Int): KtStringTemplateEntry {
|
||||
private fun KtStringTemplateEntry.unescapeIfPossible(newPrefixLength: Int): KtStringTemplateEntry {
|
||||
fun previousDollarsCount(): Int {
|
||||
if (this.prevSibling !is KtLiteralStringTemplateEntry) return 0
|
||||
return this.prevSibling.text.takeLastWhile { it == '$' }.length
|
||||
@@ -263,38 +254,47 @@ fun Char.canBeStartOfIdentifierOrBlock(): Boolean {
|
||||
return isLetter() || this == '_' || this == '{' || this == '`'
|
||||
}
|
||||
|
||||
fun KtStringTemplateEntry.isEscapedDollar(): Boolean {
|
||||
return when (this) {
|
||||
is KtEscapeStringTemplateEntry -> {
|
||||
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
|
||||
}
|
||||
|
||||
+8
@@ -68,6 +68,14 @@
|
||||
language="kotlin"
|
||||
key="inspection.can.unescape.dollar.literal.inspection.display.name" bundle="messages.KotlinBundle"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.k2.codeinsight.inspections.CanConvertToMultiDollarStringInspection"
|
||||
groupPath="Kotlin"
|
||||
groupBundle="messages.KotlinBundle" groupKey="group.names.code.migration"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
language="kotlin"
|
||||
key="inspection.can.convert.to.multi.dollar.string.display.name" bundle="messages.KotlinBundle"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.k2.codeinsight.inspections.diagnosticBased.RedundantModalityModifierInspection"
|
||||
groupPath="Kotlin"
|
||||
groupBundle="messages.KotlinBundle" groupKey="group.names.redundant.constructs"
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package org.jetbrains.kotlin.idea.k2.codeinsight.inspections
|
||||
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.codeInspection.util.InspectionMessage
|
||||
import com.intellij.codeInspection.util.IntentionFamilyName
|
||||
import com.intellij.modcommand.ModPsiUpdater
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.analysis.api.KaSession
|
||||
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.inspections.KotlinApplicableInspectionBase
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinModCommandQuickFix
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.*
|
||||
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
|
||||
import org.jetbrains.kotlin.psi.KtVisitor
|
||||
import org.jetbrains.kotlin.psi.KtVisitorVoid
|
||||
|
||||
class CanConvertToMultiDollarStringInspection :
|
||||
KotlinApplicableInspectionBase.Simple<KtStringTemplateExpression, MultiDollarConversionInfo>() {
|
||||
|
||||
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<KtStringTemplateExpression> {
|
||||
return object : KotlinModCommandQuickFix<KtStringTemplateExpression>() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-1
@@ -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
|
||||
|
||||
+134
@@ -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 {
|
||||
|
||||
+11
-8
@@ -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, Unit>(
|
||||
KtStringTemplateExpression::class,
|
||||
) {
|
||||
internal class ConvertToMultiDollarStringIntention :
|
||||
KotlinApplicableModCommandAction<KtStringTemplateExpression, MultiDollarConversionInfo>(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)
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.k2.codeinsight.inspections.CanConvertToMultiDollarStringInspection
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
"${'$'}<caret>"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
$$"$"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
"${"$"}<caret>"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
$$"$"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
"\$<caret>"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
$$"$"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
"\$ ${15 +<caret> 27}"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
$$"$ $${15 + 27}"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
"\$\$\$${'$'}${'$'}${"$"}${"$"}<caret>"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
$$"$$$$$$$"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test() {
|
||||
$$"\$<caret>"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test() {
|
||||
"\$\$\$\$\$<caret>Foo \$\$Bar"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test(some: Int) {
|
||||
"$10 10$ $$$$$ ${3 + 2} $some<caret>"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test(some: Int) {
|
||||
"""$10 10$ <caret>$$$$$ ${3 + 2} $some"""
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test(some: Int) {
|
||||
"""<caret>
|
||||
$10 10$ $$$$$ ${3 + 2} $some
|
||||
$10 10$ $$$$$ ${3 + 2} $some
|
||||
""".trimIndent()
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test() {
|
||||
"\$\$<caret>Foo \$\$\$\$\$Bar"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test() {
|
||||
"\$\$\$\$\$<caret>Foo"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test() {
|
||||
"\$\$\$\$\$<caret>`Foo`"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test() {
|
||||
"\$\$\$\$\$<caret>{Foo}"
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test() {
|
||||
"\$\$\$\$\$<caret>_Foo"
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test() {
|
||||
"""
|
||||
${'$'}${'$'}${'$'}${'$'}${'$'}Foo
|
||||
${'$'}${'$'}${'$'}${'$'}${'$'}Bar
|
||||
${'$'}${'$'}${'$'}${'$'}${'$'}Baz<caret>
|
||||
"""
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test() {
|
||||
$$"""${'$'}${'$'}${'$'}Foo<caret>"""
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
// PROBLEM: none
|
||||
|
||||
fun test() {
|
||||
"""${'$'}${'$'}${'$'}${'$'}${'$'}Foo<caret>"""
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
"${'$'}\$\$${"$"}<caret>Foo"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
$$$$$"$$$$Foo"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
"${'$'}\$\$${"$"}<caret>Foo ${'$'}\$\$${"$"}<caret>Bar"
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
$$$$$"$$$$Foo $$$$Bar"
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
"""
|
||||
\$\$${'$'}${'$'}${'$'}Foo<caret>
|
||||
\$\$${'$'}${'$'}${'$'}Bar
|
||||
\$\$${'$'}${'$'}${'$'}Baz
|
||||
"""
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
$$$$$"""
|
||||
\$\$$$$Foo
|
||||
\$\$$$$Bar
|
||||
\$\$$$$Baz
|
||||
"""
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
"""
|
||||
${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}
|
||||
${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}<caret>
|
||||
${'$'}${'$'}${'$'}${'$'}${'$'}${'$'}
|
||||
"""
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
$$"""
|
||||
$$$$$$
|
||||
$$$$$$
|
||||
$$$$$$
|
||||
"""
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
"""\$\$${'$'}${'$'}${'$'}F<caret>oo"""
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
$$$$$"""\$\$$$$Foo"""
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
"""${'$'}${'$'}${'$'}${'$'}<caret>${'$'}${'$'}"""
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// COMPILER_ARGUMENTS: -Xmulti-dollar-interpolation
|
||||
|
||||
fun test() {
|
||||
$$"""$$$$$$"""
|
||||
}
|
||||
+1
@@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
+1
@@ -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)
|
||||
}
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user