mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[kotlin] Add "'StringBuilder.append(CharArray, offset, len)' call on the JVM" inspection
^KTIJ-22712 Fixed closes https://github.com/JetBrains/intellij-community/pull/2152 GitOrigin-RevId: e8762a92a3f18685e91bb7aa007ccf731266f239
This commit is contained in:
committed by
intellij-monorepo-bot
parent
be952fc946
commit
266b0d7e7d
@@ -2496,3 +2496,5 @@ inspection.message.sealed.object.can.be.converted.to.data.object='sealed' sub-ob
|
||||
serializable.object.must.be.marked.with.data=Serializable 'object' must be marked with 'data' to properly support serialization
|
||||
convert.to.data.object=Convert to 'data object' (may change semantic)
|
||||
inspection.convert.object.to.data.object.display.name=Convert 'object' to 'data object'
|
||||
|
||||
inspection.replace.with.string.builder.append.range.display.name='StringBuilder.append(CharArray, offset, len)' call on the JVM
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports a <code>StringBuilder.append(CharArray, offset, len)</code> function call on the JVM platform that should be replaced with a <code>StringBuilder.appendRange(CharArray, startIndex, endIndex)</code> function call.
|
||||
<p>The <code>append</code> function behaves differently on the JVM, JS and Native platforms, so using the <code>appendRange</code> function is recommended.</p>
|
||||
<p><b>Example:</b></p>
|
||||
<pre><code>
|
||||
fun f(charArray: CharArray, offset: Int, len: Int): String {
|
||||
return buildString {
|
||||
append(charArray, offset, len)
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
fun f(charArray: CharArray, offset: Int, len: Int): String {
|
||||
return buildString {
|
||||
appendRange(charArray, offset, offset + len)
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
</body>
|
||||
</html>
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.kotlin.idea.inspections
|
||||
|
||||
import com.intellij.codeInspection.LocalQuickFix
|
||||
import com.intellij.codeInspection.ProblemDescriptor
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.idea.base.facet.platform.platform
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.typeUtil.isInt
|
||||
|
||||
class ReplaceWithStringBuilderAppendRangeInspection : AbstractKotlinInspection() {
|
||||
companion object {
|
||||
private const val appendFunctionName = "append"
|
||||
private const val appendRangeFunctionName = "appendRange"
|
||||
private val appendFunctionFqName = FqName("java.lang.StringBuilder.append")
|
||||
private val charArrayFqName = FqName("kotlin.CharArray")
|
||||
}
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): KtVisitorVoid =
|
||||
callExpressionVisitor(fun(callExpression: KtCallExpression) {
|
||||
if (!callExpression.platform.isJvm()) return
|
||||
val calleeExpression = callExpression.calleeExpression ?: return
|
||||
if (calleeExpression.text != appendFunctionName) return
|
||||
if (callExpression.valueArguments.size != 3) return
|
||||
|
||||
val descriptor = callExpression.resolveToCall()?.resultingDescriptor ?: return
|
||||
if (descriptor.fqNameSafe != appendFunctionFqName) return
|
||||
|
||||
val params = descriptor.valueParameters
|
||||
if (params.size != 3 || !params[0].isCharArray() || !params[1].isInt() || !params[2].isInt()) return
|
||||
|
||||
holder.registerProblem(
|
||||
calleeExpression,
|
||||
KotlinBundle.message("replace.with.0", appendRangeFunctionName),
|
||||
ReplaceFix(appendRangeFunctionName)
|
||||
)
|
||||
})
|
||||
|
||||
private fun ValueParameterDescriptor.isCharArray(): Boolean = type.fqName == charArrayFqName
|
||||
|
||||
private fun ValueParameterDescriptor.isInt(): Boolean = type.isInt()
|
||||
|
||||
private class ReplaceFix(private val functionName: String) : LocalQuickFix {
|
||||
override fun getName(): String = KotlinBundle.message("replace.with.0", functionName)
|
||||
|
||||
override fun getFamilyName(): String = name
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val calleeExpression = descriptor.psiElement as? KtExpression ?: return
|
||||
val callExpression = calleeExpression.parent as? KtCallExpression ?: return
|
||||
|
||||
val args = callExpression.valueArguments
|
||||
val secondArg = args.getOrNull(1)?.getArgumentExpression() ?: return
|
||||
val thirdArg = args.getOrNull(2)?.getArgumentExpression() ?: return
|
||||
|
||||
val psiFactory = KtPsiFactory(callExpression)
|
||||
calleeExpression.replace(psiFactory.createCalleeExpression(functionName))
|
||||
if (secondArg is KtConstantExpression && thirdArg is KtConstantExpression) {
|
||||
thirdArg.replace(psiFactory.createExpression(secondArg.text.toInt().plus(thirdArg.text.toInt()).toString()))
|
||||
} else {
|
||||
thirdArg.replace(psiFactory.createExpressionByPattern("$0 + $1", secondArg, thirdArg))
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtPsiFactory.createCalleeExpression(functionName: String): KtExpression =
|
||||
(createExpression("$functionName()") as KtCallExpression).calleeExpression!!
|
||||
}
|
||||
}
|
||||
+28
@@ -13277,6 +13277,34 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("testData/inspectionsLocal/replaceWithStringBuilderAppendRange")
|
||||
public static class ReplaceWithStringBuilderAppendRange extends AbstractLocalInspectionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("constantArgument.kt")
|
||||
public void testConstantArgument() throws Exception {
|
||||
runTest("testData/inspectionsLocal/replaceWithStringBuilderAppendRange/constantArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("hasReceiver.kt")
|
||||
public void testHasReceiver() throws Exception {
|
||||
runTest("testData/inspectionsLocal/replaceWithStringBuilderAppendRange/hasReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("precedence.kt")
|
||||
public void testPrecedence() throws Exception {
|
||||
runTest("testData/inspectionsLocal/replaceWithStringBuilderAppendRange/precedence.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("testData/inspectionsLocal/replaceWithStringBuilderAppendRange/simple.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("testData/inspectionsLocal/restrictReturnStatementTargetMigration")
|
||||
public static class RestrictReturnStatementTargetMigration extends AbstractLocalInspectionTest {
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.inspections.ReplaceWithStringBuilderAppendRangeInspection
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// WITH_STDLIB
|
||||
fun test(charArray: CharArray): String {
|
||||
return buildString {
|
||||
<caret>append(charArray, 2, 4)
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// WITH_STDLIB
|
||||
fun test(charArray: CharArray): String {
|
||||
return buildString {
|
||||
appendRange(charArray, 2, 6)
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// WITH_STDLIB
|
||||
fun test(charArray: CharArray, offset: Int, len: Int): String {
|
||||
return buildString {
|
||||
this.append<caret>(charArray, offset, len)
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// WITH_STDLIB
|
||||
fun test(charArray: CharArray, offset: Int, len: Int): String {
|
||||
return buildString {
|
||||
this.appendRange(charArray, offset, offset + len)
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// WITH_STDLIB
|
||||
fun test(charArray: CharArray, a: Int, b: Int, c: Int, d: Int): String {
|
||||
return buildString {
|
||||
<caret>append(charArray, a - b, c - d)
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// WITH_STDLIB
|
||||
fun test(charArray: CharArray, a: Int, b: Int, c: Int, d: Int): String {
|
||||
return buildString {
|
||||
appendRange(charArray, a - b, a - b + (c - d))
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// FIX: Replace with 'appendRange'
|
||||
// WITH_STDLIB
|
||||
fun test(charArray: CharArray, offset: Int, len: Int): String {
|
||||
return buildString {
|
||||
<caret>append(charArray, offset, len)
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// FIX: Replace with 'appendRange'
|
||||
// WITH_STDLIB
|
||||
fun test(charArray: CharArray, offset: Int, len: Int): String {
|
||||
return buildString {
|
||||
appendRange(charArray, offset, offset + len)
|
||||
}
|
||||
}
|
||||
@@ -2914,6 +2914,15 @@
|
||||
level="WEAK WARNING"
|
||||
language="kotlin"
|
||||
key="inspection.replace.readline.with.readln.display.name" bundle="messages.KotlinBundle"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ReplaceWithStringBuilderAppendRangeInspection"
|
||||
groupPath="Kotlin"
|
||||
groupBundle="messages.KotlinBundle" groupKey="group.names.other.problems"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
language="kotlin"
|
||||
key="inspection.replace.with.string.builder.append.range.display.name" bundle="messages.KotlinBundle"/>
|
||||
|
||||
</extensions>
|
||||
|
||||
</idea-plugin>
|
||||
Reference in New Issue
Block a user