mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[kotlin] KTIJ-19869 Add KotlinArrayToStringInspection
KTIJ-6515 KTIJ-19869 KTIJ-16730 GitOrigin-RevId: 65d5a332fb53589c4c67da28d5fdc6cda767e59e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
fbbda77200
commit
06b4848406
@@ -2646,6 +2646,12 @@ array.hash.code.problem.descriptor='hashCode()' called on array
|
||||
replace.with.content.hash.code=Replace with 'contentHashCode()'
|
||||
replace.with.content.deep.hash.code=Replace with 'contentDeepHashCode()'
|
||||
|
||||
inspection.array.to.string.display.name='toString()' called on array
|
||||
array.to.string.problem.descriptor='toString()' called on array
|
||||
array.implicit.to.string.problem.descriptor=Implicit 'toString()' called on array
|
||||
replace.with.content.to.string=Replace with 'contentToString()'
|
||||
replace.with.content.deep.to.string=Replace with 'contentDeepToString()'
|
||||
|
||||
inspection.string.referential.equality.display.name=String compared with referential equality
|
||||
string.comparison.using.referential.equality.triple.equals=String comparison using '===' instead of '=='
|
||||
string.comparison.using.referential.equality.triple.not.equals=String comparison using '!==' instead of '!='
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>Calling <code>toString()</code> on an array returns the address of the object in memory,
|
||||
not a string representation of the array's contents. This is almost never what you want.</p>
|
||||
<p>To get a string representation of an array correctly, use:</p>
|
||||
<ul>
|
||||
<li><code>contentToString()</code> for linear arrays</li>
|
||||
<li><code>contentDeepToString()</code> for multidimensional arrays</li>
|
||||
</ul>
|
||||
<p><b>Example (explicit toString):</b></p>
|
||||
<pre><code>
|
||||
fun main() {
|
||||
val a = arrayOf(1, 2, 3)
|
||||
val s = a.toString() // incorrect
|
||||
}
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
fun main() {
|
||||
val a = arrayOf(1, 2, 3)
|
||||
val s = a.contentToString() // correct
|
||||
}
|
||||
</code></pre>
|
||||
<p><b>Example (implicit toString in string template):</b></p>
|
||||
<pre><code>
|
||||
fun main() {
|
||||
val a = arrayOf(1, 2, 3)
|
||||
println("Array: $a") // incorrect - prints address
|
||||
}
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
fun main() {
|
||||
val a = arrayOf(1, 2, 3)
|
||||
println("Array: ${a.contentToString()}") // correct - prints [1, 2, 3]
|
||||
}
|
||||
</code></pre>
|
||||
<p><b>Example (multidimensional array):</b></p>
|
||||
<pre><code>
|
||||
fun main() {
|
||||
val a2d = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
|
||||
val s = a2d.toString() // incorrect
|
||||
}
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
fun main() {
|
||||
val a2d = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
|
||||
val s = a2d.contentDeepToString() // correct - prints [[1, 2], [3, 4]]
|
||||
}
|
||||
</code></pre>
|
||||
<!-- tooltip end -->
|
||||
</body>
|
||||
</html>
|
||||
+8
@@ -388,6 +388,14 @@
|
||||
language="kotlin"
|
||||
key="inspection.array.hash.code.display.name" bundle="messages.KotlinBundle"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.k2.codeinsight.inspections.KotlinArrayToStringInspection"
|
||||
groupPath="Kotlin"
|
||||
groupBundle="messages.KotlinBundle" groupKey="group.names.probable.bugs"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
language="kotlin"
|
||||
key="inspection.array.to.string.display.name" bundle="messages.KotlinBundle"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.k2.codeinsight.inspections.StringReferentialEqualityInspection"
|
||||
groupPath="Kotlin"
|
||||
groupBundle="messages.KotlinBundle" groupKey="group.names.probable.bugs"
|
||||
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
// Copyright 2000-2025 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.codeInsight.intention.HighPriorityAction
|
||||
import com.intellij.codeInspection.InspectionManager
|
||||
import com.intellij.codeInspection.ProblemDescriptor
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.codeInspection.util.IntentionFamilyName
|
||||
import com.intellij.modcommand.ModPsiUpdater
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.analysis.api.KaSession
|
||||
import org.jetbrains.kotlin.analysis.api.resolution.successfulFunctionCallOrNull
|
||||
import org.jetbrains.kotlin.analysis.api.resolution.symbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KaNamedFunctionSymbol
|
||||
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.codeinsight.api.applicators.ApplicabilityRange
|
||||
import org.jetbrains.kotlin.idea.k2.codeinsight.inspections.KotlinArrayToStringInspection.Context
|
||||
import org.jetbrains.kotlin.name.*
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
|
||||
|
||||
private val IMPLICIT_TO_STRING_METHOD_NAMES: Set<String> = setOf("append", "print", "println")
|
||||
private val TO_STRING_CALLABLE_ID = CallableId(StandardClassIds.Any, Name.identifier("toString"))
|
||||
|
||||
// This also handles Kotlin typealiases to Java underlying types
|
||||
private val IMPLICIT_TO_STRING_CALLABLE_IDS: Set<CallableId> = setOf(
|
||||
CallableId(ClassId.topLevel(FqName("java.lang.StringBuilder")), Name.identifier("append")),
|
||||
CallableId(ClassId.topLevel(FqName("java.lang.StringBuffer")), Name.identifier("append")),
|
||||
CallableId(ClassId.topLevel(FqName("java.io.PrintStream")), Name.identifier("print")),
|
||||
CallableId(ClassId.topLevel(FqName("java.io.PrintStream")), Name.identifier("println")),
|
||||
CallableId(ClassId.topLevel(FqName("java.io.PrintWriter")), Name.identifier("print")),
|
||||
CallableId(ClassId.topLevel(FqName("java.io.PrintWriter")), Name.identifier("println")),
|
||||
CallableId(FqName("kotlin.io"), Name.identifier("print")),
|
||||
CallableId(FqName("kotlin.io"), Name.identifier("println"))
|
||||
)
|
||||
|
||||
internal class KotlinArrayToStringInspection : KotlinApplicableInspectionBase<KtExpression, Context>() {
|
||||
data class Context(val isNestedArray: Boolean, val isImplicitConversion: Boolean)
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): KtVisitorVoid = object : KtVisitorVoid() {
|
||||
override fun visitQualifiedExpression(expression: KtQualifiedExpression) {
|
||||
visitTargetElement(expression, holder, isOnTheFly)
|
||||
}
|
||||
|
||||
override fun visitBinaryExpression(expression: KtBinaryExpression) {
|
||||
visitTargetElement(expression, holder, isOnTheFly)
|
||||
}
|
||||
|
||||
override fun visitCallExpression(expression: KtCallExpression) {
|
||||
visitTargetElement(expression, holder, isOnTheFly)
|
||||
}
|
||||
|
||||
override fun visitStringTemplateEntry(entry: KtStringTemplateEntry) {
|
||||
if (entry is KtStringTemplateEntryWithExpression) {
|
||||
entry.expression?.let { visitTargetElement(it, holder, isOnTheFly) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isApplicableByPsi(element: KtExpression): Boolean {
|
||||
return when (element) {
|
||||
is KtQualifiedExpression -> {
|
||||
val callExpression = element.selectorExpression as? KtCallExpression ?: return false
|
||||
if (callExpression.valueArguments.isNotEmpty()) return false
|
||||
val calleeName = callExpression.calleeExpression?.text ?: return false
|
||||
calleeName == "toString"
|
||||
}
|
||||
|
||||
is KtBinaryExpression -> {
|
||||
element.operationReference.text == "+"
|
||||
}
|
||||
|
||||
is KtCallExpression -> {
|
||||
val calleeName = element.getCallNameExpression()?.text ?: return false
|
||||
calleeName in IMPLICIT_TO_STRING_METHOD_NAMES
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Check if this is inside a string template
|
||||
val parent = element.parent
|
||||
parent is KtBlockStringTemplateEntry || parent is KtSimpleNameStringTemplateEntry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun KaSession.prepareContext(element: KtExpression): Context? {
|
||||
return when (element) {
|
||||
is KtQualifiedExpression -> {
|
||||
val receiverType = element.receiverExpression.expressionType ?: return null
|
||||
if (!receiverType.isArrayOrPrimitiveArray) return null
|
||||
val callExpression = element.selectorExpression as? KtCallExpression ?: return null
|
||||
val call = callExpression.resolveToCall()?.successfulFunctionCallOrNull() ?: return null
|
||||
val functionSymbol = call.symbol as? KaNamedFunctionSymbol ?: return null
|
||||
if (functionSymbol.callableId != TO_STRING_CALLABLE_ID) return null
|
||||
|
||||
Context(receiverType.isNestedArray, isImplicitConversion = false)
|
||||
}
|
||||
|
||||
is KtBinaryExpression -> {
|
||||
// String concatenation only works as String + Any (not Any + String)
|
||||
if (element.left?.expressionType?.isStringType != true) return null
|
||||
val rightType = element.right?.expressionType ?: return null
|
||||
if (!rightType.isArrayOrPrimitiveArray) return null
|
||||
|
||||
Context(rightType.isNestedArray, isImplicitConversion = true)
|
||||
}
|
||||
|
||||
is KtCallExpression -> {
|
||||
val call = element.resolveToCall()?.successfulFunctionCallOrNull() ?: return null
|
||||
val functionSymbol = call.symbol as? KaNamedFunctionSymbol ?: return null
|
||||
val callableId = functionSymbol.callableId ?: return null
|
||||
if (callableId !in IMPLICIT_TO_STRING_CALLABLE_IDS) return null
|
||||
|
||||
val arguments = element.valueArguments
|
||||
if (arguments.size != 1) return null
|
||||
val argType = arguments[0].getArgumentExpression()?.expressionType ?: return null
|
||||
if (!argType.isArrayOrPrimitiveArray) return null
|
||||
|
||||
Context(argType.isNestedArray, isImplicitConversion = true)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val parent = element.parent
|
||||
if (parent !is KtBlockStringTemplateEntry && parent !is KtSimpleNameStringTemplateEntry) return null
|
||||
val expressionType = element.expressionType ?: return null
|
||||
if (!expressionType.isArrayOrPrimitiveArray) return null
|
||||
|
||||
Context(expressionType.isNestedArray, isImplicitConversion = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getApplicableRanges(element: KtExpression): List<TextRange> {
|
||||
return when (element) {
|
||||
is KtQualifiedExpression -> {
|
||||
val selectorExpression = element.selectorExpression ?: return emptyList()
|
||||
ApplicabilityRange.single(element) { selectorExpression }
|
||||
}
|
||||
|
||||
is KtBinaryExpression -> {
|
||||
ApplicabilityRange.single(element) { element.operationReference }
|
||||
}
|
||||
|
||||
is KtCallExpression -> {
|
||||
val calleeExpression = element.getCallNameExpression() ?: return emptyList()
|
||||
ApplicabilityRange.single(element) { calleeExpression }
|
||||
}
|
||||
|
||||
else -> {
|
||||
// For string templates, highlight the entire expression
|
||||
ApplicabilityRange.single(element) { element }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun InspectionManager.createProblemDescriptor(
|
||||
element: KtExpression,
|
||||
context: Context,
|
||||
rangeInElement: TextRange?,
|
||||
onTheFly: Boolean
|
||||
): ProblemDescriptor {
|
||||
val message = if (context.isImplicitConversion) {
|
||||
KotlinBundle.message("array.implicit.to.string.problem.descriptor")
|
||||
} else {
|
||||
KotlinBundle.message("array.to.string.problem.descriptor")
|
||||
}
|
||||
|
||||
val fixes = if (context.isNestedArray) {
|
||||
arrayOf(ReplaceWithContentToStringFix(), ReplaceWithContentDeepToStringFix())
|
||||
} else {
|
||||
arrayOf(ReplaceWithContentToStringFix())
|
||||
}
|
||||
|
||||
return createProblemDescriptor(
|
||||
/* psiElement = */ element,
|
||||
/* rangeInElement = */ rangeInElement,
|
||||
/* descriptionTemplate = */ message,
|
||||
/* highlightType = */ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
/* onTheFly = */ onTheFly,
|
||||
/* ...fixes = */ *fixes
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ReplaceWithContentToStringFix : KotlinModCommandQuickFix<KtExpression>() {
|
||||
override fun getFamilyName(): @IntentionFamilyName String =
|
||||
KotlinBundle.message("replace.with.content.to.string")
|
||||
|
||||
override fun applyFix(project: Project, element: KtExpression, updater: ModPsiUpdater) {
|
||||
replaceWithArrayContentMethod(project, element, methodName = "contentToString")
|
||||
}
|
||||
}
|
||||
|
||||
private class ReplaceWithContentDeepToStringFix : KotlinModCommandQuickFix<KtExpression>(), HighPriorityAction {
|
||||
override fun getFamilyName(): @IntentionFamilyName String =
|
||||
KotlinBundle.message("replace.with.content.deep.to.string")
|
||||
|
||||
override fun applyFix(project: Project, element: KtExpression, updater: ModPsiUpdater) {
|
||||
replaceWithArrayContentMethod(project, element, methodName = "contentDeepToString")
|
||||
}
|
||||
}
|
||||
|
||||
private fun replaceWithArrayContentMethod(project: Project, element: KtExpression, methodName: String) {
|
||||
val psiFactory = KtPsiFactory(project)
|
||||
when (element) {
|
||||
is KtQualifiedExpression -> {
|
||||
val receiver = element.receiverExpression
|
||||
val operator = element.operationSign.value // . or ?.
|
||||
val newExpression = psiFactory.createExpressionByPattern("$0$operator$methodName()", receiver)
|
||||
element.replace(newExpression)
|
||||
}
|
||||
|
||||
is KtBinaryExpression -> {
|
||||
val left = element.left ?: return
|
||||
val right = element.right ?: return
|
||||
// Only string + array pattern is valid for string concatenation
|
||||
val newExpression = psiFactory.createExpressionByPattern("$0 + $1.$methodName()", left, right)
|
||||
element.replace(newExpression)
|
||||
}
|
||||
|
||||
is KtCallExpression -> {
|
||||
val calleeName = element.getCallNameExpression()?.text ?: return
|
||||
when (calleeName) {
|
||||
"append" -> {
|
||||
// receiver.append(array) -> receiver.append(array.contentToString())
|
||||
val receiver = element.parent as? KtDotQualifiedExpression
|
||||
if (receiver != null) {
|
||||
val receiverExpr = receiver.receiverExpression
|
||||
val argument = element.valueArguments.firstOrNull()?.getArgumentExpression() ?: return
|
||||
val operator = receiver.operationSign.value
|
||||
val newExpression = psiFactory.createExpressionByPattern(
|
||||
"$0${operator}$calleeName($1.$methodName())",
|
||||
receiverExpr,
|
||||
argument
|
||||
)
|
||||
receiver.replace(newExpression)
|
||||
} else {
|
||||
// Just append(array) without receiver -> append(array.contentToString())
|
||||
val argument = element.valueArguments.firstOrNull()?.getArgumentExpression() ?: return
|
||||
val newExpression = psiFactory.createExpressionByPattern("$calleeName($0.$methodName())", argument)
|
||||
element.replace(newExpression)
|
||||
}
|
||||
}
|
||||
|
||||
"print", "println" -> {
|
||||
// print(array) -> print(array.contentToString())
|
||||
val argument = element.valueArguments.firstOrNull()?.getArgumentExpression() ?: return
|
||||
val newExpression = psiFactory.createExpressionByPattern("$calleeName($0.$methodName())", argument)
|
||||
element.replace(newExpression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
// String template
|
||||
val parent = element.parent
|
||||
when (parent) {
|
||||
is KtBlockStringTemplateEntry -> {
|
||||
// "${array}" -> "${array.contentToString()}"
|
||||
val newExpression = psiFactory.createExpressionByPattern("$0.$methodName()", element)
|
||||
element.replace(newExpression)
|
||||
}
|
||||
is KtSimpleNameStringTemplateEntry -> {
|
||||
// "$array" -> "${array.contentToString()}"
|
||||
val newExpression = psiFactory.createExpressionByPattern("$0.$methodName()", element)
|
||||
val newEntry = psiFactory.createBlockStringTemplateEntry(newExpression)
|
||||
parent.replace(newEntry)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+109
@@ -4353,6 +4353,115 @@ public abstract class K2LocalInspectionTestGenerated extends AbstractK2LocalInsp
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../../../idea/tests/testData/inspectionsLocal/arrayToString")
|
||||
public static class ArrayToString 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("genericTypeArray.kt")
|
||||
public void testGenericTypeArray() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/genericTypeArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitNestedArrayConcatenation.kt")
|
||||
public void testImplicitNestedArrayConcatenation() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/implicitNestedArrayConcatenation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitStringConcatenation.kt")
|
||||
public void testImplicitStringConcatenation() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/implicitStringConcatenation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("intArray.kt")
|
||||
public void testIntArray() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/intArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multidimensionalArrayContentDeepToString.kt")
|
||||
public void testMultidimensionalArrayContentDeepToString() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/multidimensionalArrayContentDeepToString.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multidimensionalArrayContentToString.kt")
|
||||
public void testMultidimensionalArrayContentToString() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/multidimensionalArrayContentToString.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noInspection.kt")
|
||||
public void testNoInspection() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/noInspection.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nullableArray.kt")
|
||||
public void testNullableArray() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/nullableArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("printArray.kt")
|
||||
public void testPrintArray() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/printArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("printlnArray.kt")
|
||||
public void testPrintlnArray() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/printlnArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("printlnNestedArray.kt")
|
||||
public void testPrintlnNestedArray() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/printlnNestedArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleArray.kt")
|
||||
public void testSimpleArray() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/simpleArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringBufferAppend.kt")
|
||||
public void testStringBufferAppend() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/stringBufferAppend.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringBuilderAppend.kt")
|
||||
public void testStringBuilderAppend() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/stringBuilderAppend.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringBuilderAppendNestedArray.kt")
|
||||
public void testStringBuilderAppendNestedArray() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/stringBuilderAppendNestedArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringTemplate.kt")
|
||||
public void testStringTemplate() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/stringTemplate.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringTemplateNestedArray.kt")
|
||||
public void testStringTemplateNestedArray() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/stringTemplateNestedArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringTemplateSimpleName.kt")
|
||||
public void testStringTemplateSimpleName() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/stringTemplateSimpleName.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("varargParameter.kt")
|
||||
public void testVarargParameter() throws Exception {
|
||||
runTest("../../../idea/tests/testData/inspectionsLocal/arrayToString/varargParameter.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../../../idea/tests/testData/inspectionsLocal/stringReferentialEquality")
|
||||
public static class StringReferentialEquality extends AbstractK2LocalInspectionTest {
|
||||
|
||||
+109
@@ -192,6 +192,115 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("testData/inspectionsLocal/arrayToString")
|
||||
public static class ArrayToString extends AbstractLocalInspectionTest {
|
||||
@java.lang.Override
|
||||
@org.jetbrains.annotations.NotNull
|
||||
public final KotlinPluginMode getPluginMode() {
|
||||
return KotlinPluginMode.K1;
|
||||
}
|
||||
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("genericTypeArray.kt")
|
||||
public void testGenericTypeArray() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/genericTypeArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitNestedArrayConcatenation.kt")
|
||||
public void testImplicitNestedArrayConcatenation() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/implicitNestedArrayConcatenation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implicitStringConcatenation.kt")
|
||||
public void testImplicitStringConcatenation() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/implicitStringConcatenation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("intArray.kt")
|
||||
public void testIntArray() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/intArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multidimensionalArrayContentDeepToString.kt")
|
||||
public void testMultidimensionalArrayContentDeepToString() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/multidimensionalArrayContentDeepToString.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multidimensionalArrayContentToString.kt")
|
||||
public void testMultidimensionalArrayContentToString() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/multidimensionalArrayContentToString.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noInspection.kt")
|
||||
public void testNoInspection() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/noInspection.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nullableArray.kt")
|
||||
public void testNullableArray() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/nullableArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("printArray.kt")
|
||||
public void testPrintArray() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/printArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("printlnArray.kt")
|
||||
public void testPrintlnArray() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/printlnArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("printlnNestedArray.kt")
|
||||
public void testPrintlnNestedArray() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/printlnNestedArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleArray.kt")
|
||||
public void testSimpleArray() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/simpleArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringBufferAppend.kt")
|
||||
public void testStringBufferAppend() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/stringBufferAppend.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringBuilderAppend.kt")
|
||||
public void testStringBuilderAppend() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/stringBuilderAppend.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringBuilderAppendNestedArray.kt")
|
||||
public void testStringBuilderAppendNestedArray() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/stringBuilderAppendNestedArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringTemplate.kt")
|
||||
public void testStringTemplate() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/stringTemplate.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringTemplateNestedArray.kt")
|
||||
public void testStringTemplateNestedArray() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/stringTemplateNestedArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringTemplateSimpleName.kt")
|
||||
public void testStringTemplateSimpleName() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/stringTemplateSimpleName.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("varargParameter.kt")
|
||||
public void testVarargParameter() throws Exception {
|
||||
runTest("testData/inspectionsLocal/arrayToString/varargParameter.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("testData/inspectionsLocal/booleanLiteralArgument")
|
||||
public static class BooleanLiteralArgument extends AbstractLocalInspectionTest {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.k2.codeinsight.inspections.KotlinArrayToStringInspection
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun <T> stringifyArray(arr: Array<T>): String {
|
||||
return arr.<caret>toString()
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun <T> stringifyArray(arr: Array<T>): String {
|
||||
return arr.contentToString()
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentDeepToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
|
||||
val result = "Nested: " <caret>+ arr
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentDeepToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
|
||||
val result = "Nested: " + arr.contentDeepToString()
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(1, 2, 3)
|
||||
val result = "Array: " <caret>+ arr
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(1, 2, 3)
|
||||
val result = "Array: " + arr.contentToString()
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val a1 = intArrayOf(1, 2, 3)
|
||||
val s = a1.<caret>toString()
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val a1 = intArrayOf(1, 2, 3)
|
||||
val s = a1.contentToString()
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentDeepToString()'
|
||||
// PRIORITY: HIGH
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val a2d = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
|
||||
val s = a2d.<caret>toString()
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentDeepToString()'
|
||||
// PRIORITY: HIGH
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val a2d = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
|
||||
val s = a2d.contentDeepToString()
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val a2d = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
|
||||
val s = a2d.<caret>toString()
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val a2d = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
|
||||
val s = a2d.contentToString()
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// PROBLEM: none
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val notArray = "test"
|
||||
val s = notArray.<caret>toString()
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr: IntArray? = intArrayOf(1, 2, 3)
|
||||
val s = arr?.<caret>toString()
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr: IntArray? = intArrayOf(1, 2, 3)
|
||||
val s = arr?.contentToString()
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf("a", "b", "c")
|
||||
<caret>print(arr)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf("a", "b", "c")
|
||||
print(arr.contentToString())
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = doubleArrayOf(1.0, 2.0, 3.0)
|
||||
<caret>println(arr)
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = doubleArrayOf(1.0, 2.0, 3.0)
|
||||
println(arr.contentToString())
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentDeepToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(intArrayOf(1, 2), intArrayOf(3, 4))
|
||||
<caret>println(arr)
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentDeepToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(intArrayOf(1, 2), intArrayOf(3, 4))
|
||||
println(arr.contentDeepToString())
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val a = arrayOf(1, 2, 3)
|
||||
val s = a.<caret>toString()
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val a = arrayOf(1, 2, 3)
|
||||
val s = a.contentToString()
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = intArrayOf(1, 2, 3)
|
||||
val sb = StringBuffer()
|
||||
sb.<caret>append(arr)
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = intArrayOf(1, 2, 3)
|
||||
val sb = StringBuffer()
|
||||
sb.append(arr.contentToString())
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(1, 2, 3)
|
||||
val sb = StringBuilder()
|
||||
sb.<caret>append(arr)
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(1, 2, 3)
|
||||
val sb = StringBuilder()
|
||||
sb.append(arr.contentToString())
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentDeepToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
|
||||
val sb = StringBuilder()
|
||||
sb.<caret>append(arr)
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentDeepToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(arrayOf(1, 2), arrayOf(3, 4))
|
||||
val sb = StringBuilder()
|
||||
sb.append(arr.contentDeepToString())
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(1, 2, 3)
|
||||
val result = "Array: ${<caret>arr}"
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(1, 2, 3)
|
||||
val result = "Array: ${arr.contentToString()}"
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentDeepToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(arrayOf("a", "b"), arrayOf("c", "d"))
|
||||
val result = "Nested: ${<caret>arr}"
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentDeepToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(arrayOf("a", "b"), arrayOf("c", "d"))
|
||||
val result = "Nested: ${arr.contentDeepToString()}"
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(1, 2, 3)
|
||||
val result = "Array: $<caret>arr"
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: Implicit 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun main() {
|
||||
val arr = arrayOf(1, 2, 3)
|
||||
val result = "Array: ${arr.contentToString()}"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun printItems(vararg items: Int) {
|
||||
val s = items.<caret>toString()
|
||||
println(s)
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// PROBLEM: 'toString()' called on array
|
||||
// FIX: Replace with 'contentToString()'
|
||||
// IGNORE_K1
|
||||
// WITH_STDLIB
|
||||
|
||||
fun printItems(vararg items: Int) {
|
||||
val s = items.contentToString()
|
||||
println(s)
|
||||
}
|
||||
+1
@@ -60,6 +60,7 @@ internal fun MutableTWorkspace.generateK2InspectionTests() {
|
||||
model("${idea}/inspectionsLocal/redundantRequireNotNullCall")
|
||||
model("${idea}/inspectionsLocal/suspiciousCallOnCollectionToAddOrRemovePath")
|
||||
model("${idea}/inspectionsLocal/arrayHashCode")
|
||||
model("${idea}/inspectionsLocal/arrayToString")
|
||||
model("${idea}/inspectionsLocal/stringReferentialEquality")
|
||||
model("${idea}/inspectionsLocal/suspiciousCascadingIf")
|
||||
model("${idea}/inspectionsLocal/equalsOrHashCode")
|
||||
|
||||
Reference in New Issue
Block a user