[kotlin] KTIJ-19869 Add KotlinArrayToStringInspection

KTIJ-6515
KTIJ-19869
KTIJ-16730

GitOrigin-RevId: 65d5a332fb53589c4c67da28d5fdc6cda767e59e
This commit is contained in:
Alexey Belkov
2025-11-09 20:44:29 +00:00
committed by intellij-monorepo-bot
parent fbbda77200
commit 06b4848406
45 changed files with 902 additions and 0 deletions
@@ -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 '!='
@@ -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>
@@ -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"
@@ -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)
}
}
}
}
}
@@ -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 {
@@ -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()
}
@@ -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()
}
@@ -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
}
@@ -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()
}
@@ -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
}
@@ -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()
}
@@ -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()
}
@@ -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()
}
@@ -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()
}
@@ -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()
}
@@ -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)
}
@@ -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())
}
@@ -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)
}
@@ -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()
}
@@ -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)
}
@@ -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())
}
@@ -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)
}
@@ -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())
}
@@ -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)
}
@@ -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}"
}
@@ -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: 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}"
}
@@ -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()}"
}
@@ -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"
}
@@ -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)
}
@@ -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)
}
@@ -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")