[kotlin] KTIJ-37274 ReplaceManualRangeWithIndicesCallsInspection support withIndex replacement

GitOrigin-RevId: bbb044e2db9897009cce15cb083e618666f23baf
This commit is contained in:
Olga Klisho
2026-02-16 20:13:36 +00:00
committed by intellij-monorepo-bot
parent 5792d62c48
commit c8b48a46eb
37 changed files with 395 additions and 88 deletions
@@ -1688,6 +1688,8 @@ replace.index.loop.with.collection.loop.quick.fix.text=Replace with loop over el
replace.manual.range.with.indices.call.quick.fix.text=Replace with indices
range.could.be.replaced.with.indices.call=Range could be replaced with '.indices' call
for.loop.over.indices.could.be.replaced.with.loop.over.elements=For loop over indices could be replaced with loop over elements
for.loop.could.use.withIndex=For loop could use 'withIndex()'
replace.index.loop.with.withIndex.quick.fix.text=Replace with 'withIndex()'
replace.negated.0.with.1=Replace negated ''{0}'' with ''{1}''
replace.with.elvis.return.fix.text=Replace with ''?: return{0}''
replace.with.return=Replace '!!' with '?: return'
@@ -9861,6 +9861,26 @@ public abstract class K2LocalInspectionTestGenerated extends AbstractK2LocalInsp
public void testUsedForOtherArray() throws Exception {
runTest("../../../idea/tests/testData/inspectionsLocal/replaceManualRangeWithIndicesCalls/usedForOtherArray.kt");
}
@TestMetadata("withIndexBasic.kt")
public void testWithIndexBasic() throws Exception {
runTest("../../../idea/tests/testData/inspectionsLocal/replaceManualRangeWithIndicesCalls/withIndexBasic.kt");
}
@TestMetadata("withIndexElementNameCollision.kt")
public void testWithIndexElementNameCollision() throws Exception {
runTest("../../../idea/tests/testData/inspectionsLocal/replaceManualRangeWithIndicesCalls/withIndexElementNameCollision.kt");
}
@TestMetadata("withIndexMultipleArrayAccess.kt")
public void testWithIndexMultipleArrayAccess() throws Exception {
runTest("../../../idea/tests/testData/inspectionsLocal/replaceManualRangeWithIndicesCalls/withIndexMultipleArrayAccess.kt");
}
@TestMetadata("withIndexRangeTo.kt")
public void testWithIndexRangeTo() throws Exception {
runTest("../../../idea/tests/testData/inspectionsLocal/replaceManualRangeWithIndicesCalls/withIndexRangeTo.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@@ -1,12 +1,20 @@
// 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.codeInsight.inspections.shared
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.InspectionMessage
import com.intellij.codeInspection.util.IntentionFamilyName
import com.intellij.modcommand.ModPsiUpdater
import com.intellij.openapi.project.Project
import com.intellij.psi.SmartPsiElementPointer
import com.intellij.psi.createSmartPointer
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.analysis.api.resolution.singleFunctionCallOrNull
import org.jetbrains.kotlin.analysis.api.resolution.symbol
import org.jetbrains.kotlin.analysis.api.resolution.successfulVariableAccessCall
import org.jetbrains.kotlin.analysis.api.types.KaType
import org.jetbrains.kotlin.idea.base.codeInsight.KotlinDeclarationNameValidator
@@ -15,6 +23,9 @@ import org.jetbrains.kotlin.idea.base.codeInsight.KotlinNameSuggestionProvider
import org.jetbrains.kotlin.idea.base.psi.replaced
import org.jetbrains.kotlin.idea.base.psi.safeDeparenthesize
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.codeInsight.inspections.shared.AbstractRangeInspection.Companion.rangeExpressionByPsi
import org.jetbrains.kotlin.idea.codeInsight.inspections.shared.AbstractRangeInspection.RangeExpression
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.utils.ImplicitReceiverInfo
import org.jetbrains.kotlin.idea.codeinsight.utils.LoopToCollectionTransformUtils
@@ -25,6 +36,7 @@ import org.jetbrains.kotlin.idea.codeinsight.utils.RangeKtExpressionType.RANGE_U
import org.jetbrains.kotlin.idea.codeinsight.utils.RangeKtExpressionType.UNTIL
import org.jetbrains.kotlin.idea.codeinsight.utils.getImplicitReceiverInfo
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBinaryExpression
@@ -39,6 +51,8 @@ import org.jetbrains.kotlin.psi.KtPsiUtil
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.KtThisExpression
import org.jetbrains.kotlin.psi.KtVisitor
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.createExpressionByPattern
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents
@@ -47,26 +61,56 @@ import org.jetbrains.kotlin.util.match
/**
* Inspection that suggests replacing manual ranges like `0..<arr.size` with `arr.indices` calls.
* Extends KotlinApplicableInspectionBase directly (not Simple) to support multiple quick fixes.
*/
class ReplaceManualRangeWithIndicesCallsInspection : AbstractRangeInspection<ReplaceManualRangeWithIndicesCallsInspection.Context>() {
class ReplaceManualRangeWithIndicesCallsInspection : KotlinApplicableInspectionBase<KtExpression, ReplaceManualRangeWithIndicesCallsInspection.Context>() {
/**
* Describes how the loop index variable is used in the loop body,
* determining which quick fixes are applicable.
*/
enum class IndexUsagePattern {
/**
* Index is only used for array access: `arr[i]`.
* Can transform to: `for (element in arr)`
*/
ELEMENT_LOOP,
/**
* Index is used both for array access and other purposes: `println("$i: ${arr[i]}")`.
* Can transform to: `for ((i, element) in arr.withIndex())`
*/
WITH_INDEX,
/**
* Index is not used for array access, or range is not inside a for loop.
* Can only replace range with `.indices`: `for (i in arr.indices)`
*/
INDICES_ONLY
}
data class Context(
val explicitReceiver: KtExpression?,
val suggestElementLoop: Boolean,
val indexUsagePattern: IndexUsagePattern,
val implicitReceiverInfo: ImplicitReceiverInfo?,
val suggestedElementName: String?,
val arrayAccessUsages: List<SmartPsiElementPointer<KtArrayAccessExpression>>,
)
override fun getProblemDescription(
range: RangeExpression,
context: Context
): @InspectionMessage String {
return if (context.suggestElementLoop) {
KotlinBundle.message("for.loop.over.indices.could.be.replaced.with.loop.over.elements")
} else KotlinBundle.message("range.could.be.replaced.with.indices.call")
private fun getProblemDescription(context: Context): @InspectionMessage String {
return when (context.indexUsagePattern) {
IndexUsagePattern.ELEMENT_LOOP -> KotlinBundle.message("for.loop.over.indices.could.be.replaced.with.loop.over.elements")
IndexUsagePattern.WITH_INDEX -> KotlinBundle.message("for.loop.could.use.withIndex")
IndexUsagePattern.INDICES_ONLY -> KotlinBundle.message("range.could.be.replaced.with.indices.call")
}
}
override fun isApplicableByPsi(range: RangeExpression): Boolean {
override fun isApplicableByPsi(element: KtExpression): Boolean {
val range = rangeExpressionByPsi(element) ?: return false
return isApplicableByPsi(range)
}
private fun isApplicableByPsi(range: RangeExpression): Boolean {
// Only ascending ranges are candidates for indices replacement
if (range.type == DOWN_TO) return false
@@ -81,18 +125,34 @@ class ReplaceManualRangeWithIndicesCallsInspection : AbstractRangeInspection<Rep
return extractTargetExpression(range.type, right) != null
}
override fun KaSession.prepareContext(range: RangeExpression): Context? {
override fun KaSession.prepareContext(element: KtExpression): Context? {
val range = rangeExpressionByAnalyze(element) ?: return null
return prepareContextForRange(range)
}
/**
* Returns a [RangeExpression] only if it resolves to a Kotlin stdlib range function.
* This prevents false positives when custom `until`/`rangeTo` functions are defined.
*/
private fun KaSession.rangeExpressionByAnalyze(expression: KtExpression): RangeExpression? =
rangeExpressionByPsi(expression)?.takeIf {
val call = expression.resolveToCall()?.singleFunctionCallOrNull()
val packageName = call?.symbol?.callableId?.packageName
packageName != null && packageName.startsWith(Name.identifier("kotlin"))
}
private fun KaSession.prepareContextForRange(range: RangeExpression): Context? {
val (_, right) = range.arguments
// Must end with a size/length call
val sizeCall = right?.let { rightBound(range.type, it) } ?: return null
val explicitReceiver = (sizeCall as? KtQualifiedExpression)?.receiverExpression
val suggestElementLoop = shouldSuggestElementLoop(range, explicitReceiver)
val indexUsageAnalysis = analyzeIndexUsagePattern(range, explicitReceiver)
val implicitReceiverInfo = if (explicitReceiver == null) sizeCall.getImplicitReceiverInfo() else null
// Pre-compute a unique element name for the loop transformation
val suggestedElementName = if (suggestElementLoop) {
// Pre-compute a unique element name for the loop transformation (for ELEMENT_LOOP and WITH_INDEX patterns)
val suggestedElementName = if (indexUsageAnalysis.pattern != IndexUsagePattern.INDICES_ONLY) {
val forExpression = findContainingForLoop(range)
forExpression?.loopParameter?.let { loopParameter ->
val nameValidator = KotlinDeclarationNameValidator(
@@ -104,7 +164,16 @@ class ReplaceManualRangeWithIndicesCallsInspection : AbstractRangeInspection<Rep
}
} else null
return Context(explicitReceiver, suggestElementLoop, implicitReceiverInfo, suggestedElementName)
// Store array access usages as smart pointers for use in quick fixes
val arrayAccessPointers = indexUsageAnalysis.arrayAccessUsages.map { it.createSmartPointer() }
return Context(
explicitReceiver,
indexUsageAnalysis.pattern,
implicitReceiverInfo,
suggestedElementName,
arrayAccessPointers
)
}
private fun KaSession.rightBound(type: RangeKtExpressionType, expression: KtExpression): KtExpression? {
@@ -161,22 +230,50 @@ class ReplaceManualRangeWithIndicesCallsInspection : AbstractRangeInspection<Rep
}
/**
* Determines if we should suggest converting the index loop to an element loop.
* This checks if the loop parameter is used only as array index(es) with the same receiver.
* Result of analyzing how the loop index variable is used in the loop body.
*/
private fun shouldSuggestElementLoop(range: RangeExpression, explicitReceiver: KtExpression?): Boolean {
val forExpression = findContainingForLoop(range) ?: return false
val loopParam = forExpression.loopParameter?.originalElement ?: return false
private data class IndexUsageAnalysis(
val pattern: IndexUsagePattern,
val arrayAccessUsages: List<KtArrayAccessExpression>,
)
/**
* Analyzes how the loop index variable is used in the loop body.
*
* @return IndexUsageAnalysis containing:
* - pattern: ELEMENT_LOOP (all array accesses), WITH_INDEX (mixed), or INDICES_ONLY (no array accesses)
* - arrayAccessUsages: list of valid array access expressions for transformation
*/
private fun analyzeIndexUsagePattern(range: RangeExpression, explicitReceiver: KtExpression?): IndexUsageAnalysis {
val forExpression = findContainingForLoop(range)
?: return IndexUsageAnalysis(IndexUsagePattern.INDICES_ONLY, emptyList())
val loopParam = forExpression.loopParameter?.originalElement
?: return IndexUsageAnalysis(IndexUsagePattern.INDICES_ONLY, emptyList())
val usages = ReferencesSearch.search(loopParam).findAll()
if (usages.isEmpty()) return false
if (usages.isEmpty()) return IndexUsageAnalysis(IndexUsagePattern.INDICES_ONLY, emptyList())
// Check that all usages are valid array accesses with the same receiver
return usages.all { reference ->
val validArrayAccesses = mutableListOf<KtArrayAccessExpression>()
var otherUsageCount = 0
for (reference in usages) {
val usage = reference.element
val arrayAccess = usage.parents.match(KtContainerNode::class, last = KtArrayAccessExpression::class)
arrayAccess != null && isValidElementLoopCandidate(arrayAccess, explicitReceiver)
if (arrayAccess != null && isValidElementLoopCandidate(arrayAccess, explicitReceiver)) {
validArrayAccesses.add(arrayAccess)
} else {
otherUsageCount++
}
}
val pattern = when {
validArrayAccesses.isNotEmpty() && otherUsageCount == 0 -> IndexUsagePattern.ELEMENT_LOOP
validArrayAccesses.isNotEmpty() && otherUsageCount > 0 -> IndexUsagePattern.WITH_INDEX
else -> IndexUsagePattern.INDICES_ONLY
}
return IndexUsageAnalysis(pattern, validArrayAccesses)
}
private fun findContainingForLoop(range: RangeExpression): KtForExpression? =
@@ -203,12 +300,41 @@ class ReplaceManualRangeWithIndicesCallsInspection : AbstractRangeInspection<Rep
return parent !is KtBinaryExpression || parent.left != arrayAccess || parent.operationToken !in KtTokens.ALL_ASSIGNMENTS
}
override fun createQuickFix(
range: RangeExpression,
context: Context
): KotlinModCommandQuickFix<KtExpression> {
return if (context.suggestElementLoop) ReplaceIndexLoopWithCollectionLoopQuickFix(context)
else ReplaceManualRangeWithIndicesCallQuickFix(context)
private fun createQuickFixes(context: Context): List<KotlinModCommandQuickFix<KtExpression>> {
val indicesFix = ReplaceManualRangeWithIndicesCallQuickFix(context)
return when (context.indexUsagePattern) {
IndexUsagePattern.ELEMENT_LOOP -> listOf(ReplaceIndexLoopWithCollectionLoopQuickFix(context), indicesFix)
IndexUsagePattern.WITH_INDEX -> listOf(ReplaceWithWithIndexLoopQuickFix(context), indicesFix)
IndexUsagePattern.INDICES_ONLY -> listOf(indicesFix)
}
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): KtVisitor<*, *> =
object : KtVisitorVoid() {
override fun visitBinaryExpression(expression: KtBinaryExpression) {
visitTargetElement(expression, holder, isOnTheFly)
}
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
visitTargetElement(expression, holder, isOnTheFly)
}
}
override fun InspectionManager.createProblemDescriptor(
element: KtExpression,
context: Context,
rangeInElement: com.intellij.openapi.util.TextRange?,
onTheFly: Boolean,
): ProblemDescriptor {
val fixes = createQuickFixes(context)
return createProblemDescriptor(
element,
rangeInElement,
getProblemDescription(context),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
onTheFly,
*fixes.toTypedArray()
)
}
/**
@@ -264,29 +390,14 @@ class ReplaceManualRangeWithIndicesCallsInspection : AbstractRangeInspection<Rep
val loopRange = forExpression.loopRange ?: return
val elementName = context.suggestedElementName ?: return
// Get writable array accesses from smart pointers
val arrayAccesses = context.arrayAccessUsages.mapNotNull { pointer ->
pointer.element?.let { updater.getWritable(it) }
}
if (arrayAccesses.isEmpty()) return
val collection = resolveCollectionExpression(project)
// Try a single usage first
val singleUsageInfo = LoopToCollectionTransformUtils.findSingleArrayAccessUsage(loopParameter)
if (singleUsageInfo != null) {
LoopToCollectionTransformUtils.transformLoop(project, listOf(singleUsageInfo), loopParameter, loopRange, collection, elementName)
return
}
// Handle multiple usages
val loopParam = loopParameter.originalElement ?: return
val usages = ReferencesSearch.search(loopParam).findAll()
val usageInfos = usages.mapNotNull { reference ->
val usage = reference.element
val arrayAccess = usage.parents.match(KtContainerNode::class, last = KtArrayAccessExpression::class)
if (arrayAccess != null) {
LoopToCollectionTransformUtils.LoopUsageInfo(loopParameter, usage, arrayAccess)
} else null
}
if (usageInfos.isNotEmpty()) {
LoopToCollectionTransformUtils.transformLoop(project, usageInfos, loopParameter, loopRange, collection, elementName)
}
LoopToCollectionTransformUtils.transformLoop(project, arrayAccesses, loopParameter, loopRange, collection, elementName)
}
/**
@@ -306,4 +417,40 @@ class ReplaceManualRangeWithIndicesCallsInspection : AbstractRangeInspection<Rep
return KtPsiFactory(project).createThisExpression()
}
}
private inner class ReplaceWithWithIndexLoopQuickFix(
private val context: Context,
) : KotlinModCommandQuickFix<KtExpression>() {
override fun getFamilyName(): @IntentionFamilyName String =
KotlinBundle.message("replace.index.loop.with.withIndex.quick.fix.text")
override fun applyFix(project: Project, element: KtExpression, updater: ModPsiUpdater) {
val forExpression = element.getStrictParentOfType<KtForExpression>() ?: return
val loopParameter = forExpression.loopParameter ?: return
val loopRange = forExpression.loopRange ?: return
val elementName = context.suggestedElementName ?: return
// Get writable array accesses from smart pointers
val arrayAccesses = context.arrayAccessUsages.mapNotNull { pointer ->
pointer.element?.let { updater.getWritable(it) }
}
if (arrayAccesses.isEmpty()) return
val collection = resolveCollectionExpression(project)
LoopToCollectionTransformUtils.transformLoopWithIndex(
project, arrayAccesses, loopParameter, loopRange, collection, elementName
)
}
private fun resolveCollectionExpression(project: Project): KtExpression {
context.explicitReceiver?.let { return it }
context.implicitReceiverInfo?.takeUnless { it.isUnambiguousLabel }?.let {
return createImplicitThis(project, it)
}
return KtPsiFactory(project).createThisExpression()
}
}
}
@@ -2,16 +2,14 @@
package org.jetbrains.kotlin.idea.codeinsight.utils
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.search.searches.ReferencesSearch
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtContainerNode
import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.util.match
import org.jetbrains.kotlin.psi.createExpressionByPattern
import org.jetbrains.kotlin.psi.psiUtil.canPlaceAfterSimpleNameEntry
/**
* Utility functions for transforming index-based loops to collection loops.
@@ -20,36 +18,13 @@ import org.jetbrains.kotlin.util.match
object LoopToCollectionTransformUtils {
/**
* Data class representing the result of finding loop usage patterns.
*/
data class LoopUsageInfo(
val paramElement: KtParameter,
val usageElement: PsiElement,
val arrayAccessElement: KtArrayAccessExpression
)
/**
* Finds the usage pattern for a loop parameter where it's used as an array index exactly once.
*
* @param loopParameter the loop parameter (e.g., `i` in `for (i in 0..<size)`)
* @return LoopUsageInfo if the parameter is used exactly once as an array index, null otherwise
*/
fun findSingleArrayAccessUsage(loopParameter: KtParameter): LoopUsageInfo? {
val paramElement = loopParameter.originalElement ?: return null
val usageElement = ReferencesSearch.search(paramElement).findAll().singleOrNull()?.element ?: return null
val arrayAccessElement = usageElement.parents.match(KtContainerNode::class, last = KtArrayAccessExpression::class) ?: return null
return LoopUsageInfo(paramElement as KtParameter, usageElement, arrayAccessElement)
}
/**
* Transforms an index-based loop to a collection-based loop with multiple array access usages by:
* Transforms an index-based loop to a collection-based loop by:
* 1. Replacing the loop parameter with the provided element name
* 2. Replacing all array access expressions with direct element references
* 3. Replacing the loop range with the collection expression
*
* @param project the current project
* @param usageInfos information about all loop usage patterns
* @param arrayAccesses array access expressions to replace with element references
* @param loopParameter the original loop parameter
* @param loopRange the original loop range
* @param newLoopRange the new collection expression to iterate over
@@ -57,7 +32,7 @@ object LoopToCollectionTransformUtils {
*/
fun transformLoop(
project: Project,
usageInfos: List<LoopUsageInfo>,
arrayAccesses: List<KtArrayAccessExpression>,
loopParameter: KtParameter,
loopRange: KtExpression,
newLoopRange: KtExpression,
@@ -66,11 +41,58 @@ object LoopToCollectionTransformUtils {
val factory = KtPsiFactory(project)
val newParameter = factory.createLoopParameter(elementName)
val newReferenceExpression = factory.createExpression(elementName)
// Replace all array access expressions with direct element references
usageInfos.forEach { usageInfo ->
usageInfo.arrayAccessElement.replace(newReferenceExpression.copy())
arrayAccesses.forEach { arrayAccess ->
replaceArrayAccessWithElement(factory, arrayAccess, elementName)
}
loopParameter.replace(newParameter)
loopRange.replace(newLoopRange)
}
/**
* Replaces an array access expression with a simple element reference.
* When inside a string template ${...}, creates the simplified $name form directly if possible.
*/
private fun replaceArrayAccessWithElement(factory: KtPsiFactory, arrayAccess: KtArrayAccessExpression, elementName: String) {
val blockEntry = arrayAccess.parent as? KtBlockStringTemplateEntry
if (blockEntry != null && canPlaceAfterSimpleNameEntry(blockEntry.nextSibling)) {
blockEntry.replace(factory.createSimpleNameStringTemplateEntry(elementName))
} else {
arrayAccess.replace(factory.createExpression(elementName))
}
}
/**
* Transforms an index-based loop to a withIndex() loop by:
* 1. Replacing the loop parameter with a destructuring declaration (indexName, elementName)
* 2. Replacing array access expressions with direct element references
* 3. Replacing the loop range with collection.withIndex()
* @param project the current project
* @param arrayAccesses array access expressions to replace with element references
* @param loopParameter the original loop parameter (the index variable)
* @param loopRange the original loop range
* @param collectionExpression the collection to iterate with withIndex()
* @param elementName the name for the new element variable
*/
fun transformLoopWithIndex(
project: Project,
arrayAccesses: List<KtArrayAccessExpression>,
loopParameter: KtParameter,
loopRange: KtExpression,
collectionExpression: KtExpression,
elementName: String
) {
val factory = KtPsiFactory(project)
val indexName = loopParameter.name ?: "index"
val newParameter = factory.createDestructuringParameter("($indexName, $elementName)")
val newLoopRange = factory.createExpressionByPattern("$0.withIndex()", collectionExpression)
// Replace all array access expressions with direct element references
arrayAccesses.forEach { arrayAccess ->
replaceArrayAccessWithElement(factory, arrayAccess, elementName)
}
loopParameter.replace(newParameter)
@@ -14545,6 +14545,26 @@ public abstract class LocalInspectionTestGenerated extends AbstractK1LocalInspec
public void testUsedForOtherArray() throws Exception {
runTest("testData/inspectionsLocal/replaceManualRangeWithIndicesCalls/usedForOtherArray.kt");
}
@TestMetadata("withIndexBasic.kt")
public void testWithIndexBasic() throws Exception {
runTest("testData/inspectionsLocal/replaceManualRangeWithIndicesCalls/withIndexBasic.kt");
}
@TestMetadata("withIndexElementNameCollision.kt")
public void testWithIndexElementNameCollision() throws Exception {
runTest("testData/inspectionsLocal/replaceManualRangeWithIndicesCalls/withIndexElementNameCollision.kt");
}
@TestMetadata("withIndexMultipleArrayAccess.kt")
public void testWithIndexMultipleArrayAccess() throws Exception {
runTest("testData/inspectionsLocal/replaceManualRangeWithIndicesCalls/withIndexMultipleArrayAccess.kt");
}
@TestMetadata("withIndexRangeTo.kt")
public void testWithIndexRangeTo() throws Exception {
runTest("testData/inspectionsLocal/replaceManualRangeWithIndicesCalls/withIndexRangeTo.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
class Person(val name: String)
fun getName(person: Person) {
for (i in 0 until<caret> person.name.length) {
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
class Person(val name: String)
fun getName(person: Person) {
for (element in person.name) {
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
// IGNORE_K1
fun test() {
val element = mutableListOf<String>("hello", "world")
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
// IGNORE_K1
fun test() {
val element = mutableListOf<String>("hello", "world")
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
// IGNORE_K1
fun test() {
val element = "first"
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
// IGNORE_K1
fun test() {
val element = "first"
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
// IGNORE_K1
fun test() {
val outer = listOf(listOf("a", "b"), listOf("c", "d"))
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
// IGNORE_K1
fun test() {
val outer = listOf(listOf("a", "b"), listOf("c", "d"))
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
// IGNORE_K1
fun test(element: String, list: List<String>) {
for (i in 0 unt<caret>il list.size) {
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
// IGNORE_K1
fun test(element: String, list: List<String>) {
for (element1 in list) {
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
fun Array<String>.test() {
for (index in <caret>0..size - 1) {
val out = this[index]
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
fun Array<String>.test() {
for (element in this) {
val out = element
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
fun foo() {
val intArray = intArrayOf(1, 2, 3, 4, 5)
for (i in 0.<caret>.intArray.lastIndex) {
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
fun foo() {
val intArray = intArrayOf(1, 2, 3, 4, 5)
for (element in intArray) {
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
fun IntArray.arrayToString(): String = buildString {
for (i in <caret>0 until size) {
append(this@arrayToString[i])
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
fun IntArray.arrayToString(): String = buildString {
for (element in this@arrayToString) {
append(element)
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with 'withIndex()'
fun LongArray.arrayToString(): String = buildString {
for (i in <caret>0 until size) {
if (i > 0) append(", ")
@@ -1,7 +1,8 @@
// WITH_STDLIB
// FIX: Replace with 'withIndex()'
fun LongArray.arrayToString(): String = buildString {
for (i in this@arrayToString.indices) {
for ((i, element) in this@arrayToString.withIndex()) {
if (i > 0) append(", ")
append(this@arrayToString[i])
append(element)
}
}
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
fun test(args: Array<String>) {
for (index in 0..ar<caret>gs.size - 1) {
val out = args[index]
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
fun test(args: Array<String>) {
for (element in args) {
val out = element
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
fun main() {
println(listOf(1, 2, 3).doubled())
}
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
fun main() {
println(listOf(1, 2, 3).doubled())
}
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
fun Array<String>.test() {
for (index in <caret>0..this.size - 1) {
val out = this[index]
@@ -1,4 +1,5 @@
// WITH_STDLIB
// FIX: Replace with loop over elements
fun Array<String>.test() {
for (element in this) {
val out = element
@@ -0,0 +1,8 @@
// WITH_STDLIB
// FIX: Replace with 'withIndex()'
fun test() {
val list = listOf("a", "b", "c")
for (i in 0 unt<caret>il list.size) {
println("Index $i: ${list[i]}")
}
}
@@ -0,0 +1,8 @@
// WITH_STDLIB
// FIX: Replace with 'withIndex()'
fun test() {
val list = listOf("a", "b", "c")
for ((i, element) in list.withIndex()) {
println("Index $i: $element")
}
}
@@ -0,0 +1,10 @@
// WITH_STDLIB
// FIX: Replace with 'withIndex()'
// IGNORE_K1
fun test() {
val element = "existing"
val list = listOf("a", "b", "c")
for (i in 0 unt<caret>il list.size) {
println("Index $i: ${list[i]}, also $element")
}
}
@@ -0,0 +1,10 @@
// WITH_STDLIB
// FIX: Replace with 'withIndex()'
// IGNORE_K1
fun test() {
val element = "existing"
val list = listOf("a", "b", "c")
for ((i, element1) in list.withIndex()) {
println("Index $i: $element1, also $element")
}
}
@@ -0,0 +1,10 @@
// WITH_STDLIB
// FIX: Replace with 'withIndex()'
fun test() {
val list = listOf("a", "b", "c")
for (i in 0..<caret><list.size) {
println("Processing index $i")
val item = list[i]
println("Item: $item, also ${list[i].length}")
}
}
@@ -0,0 +1,10 @@
// WITH_STDLIB
// FIX: Replace with 'withIndex()'
fun test() {
val list = listOf("a", "b", "c")
for ((i, element) in list.withIndex()) {
println("Processing index $i")
val item = element
println("Item: $item, also ${element.length}")
}
}
@@ -0,0 +1,8 @@
// WITH_STDLIB
// FIX: Replace with 'withIndex()'
fun test() {
val arr = arrayOf(1, 2, 3)
for (idx in 0.<caret>.arr.size - 1) {
println("arr[$idx] = ${arr[idx]}")
}
}
@@ -0,0 +1,8 @@
// WITH_STDLIB
// FIX: Replace with 'withIndex()'
fun test() {
val arr = arrayOf(1, 2, 3)
for ((idx, element) in arr.withIndex()) {
println("arr[$idx] = $element")
}
}