For each parameter not used: introduce "replace with repeat"
An enhancement for KT-27209
This commit is contained in:
+34
-1
@@ -11,14 +11,20 @@ import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl.WithDestructuringDeclaration
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.core.moveFunctionLiteralOutsideParentheses
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.intentions.SpecifyExplicitLambdaSignatureIntention
|
||||
import org.jetbrains.kotlin.idea.project.builtIns
|
||||
import org.jetbrains.kotlin.idea.refactoring.getThisLabelName
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
@@ -45,11 +51,16 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() {
|
||||
if (iterableParameter !is WithDestructuringDeclaration &&
|
||||
!lambda.bodyExpression.usesDescriptor(iterableParameter, context)
|
||||
) {
|
||||
val fixes = mutableListOf<LocalQuickFix>()
|
||||
if (it.parent is KtDotQualifiedExpression) {
|
||||
fixes += ReplaceWithRepeatFix()
|
||||
}
|
||||
fixes += IntroduceAnonymousParameterFix()
|
||||
holder.registerProblem(
|
||||
calleeExpression,
|
||||
"Loop parameter '${iterableParameter.getThisLabelName()}' is unused",
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
IntroduceAnonymousParameterFix()
|
||||
*fixes.toTypedArray()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -71,6 +82,28 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
private class ReplaceWithRepeatFix : LocalQuickFix {
|
||||
override fun getFamilyName() = "Replace with 'repeat(size)'"
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val callExpression = descriptor.psiElement.parent as? KtCallExpression ?: return
|
||||
val qualifiedExpression = callExpression.parent as? KtDotQualifiedExpression ?: return
|
||||
val receiverExpression = qualifiedExpression.receiverExpression
|
||||
val receiverClass =
|
||||
receiverExpression.resolveToCall()?.resultingDescriptor?.returnType?.constructor?.declarationDescriptor as? ClassDescriptor
|
||||
val collection = callExpression.builtIns.collection
|
||||
val sizeText = when {
|
||||
receiverClass != null && DescriptorUtils.isSubclass(receiverClass, collection) -> "size"
|
||||
else -> "count()"
|
||||
}
|
||||
val lambdaExpression = callExpression.lambdaArguments.singleOrNull()?.getArgumentExpression() ?: return
|
||||
val replacement =
|
||||
KtPsiFactory(project).createExpressionByPattern("repeat($0.$sizeText, $1)", receiverExpression, lambdaExpression)
|
||||
val result = qualifiedExpression.replaced(replacement) as KtCallExpression
|
||||
result.moveFunctionLiteralOutsideParentheses()
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtBlockExpression?.usesDescriptor(descriptor: VariableDescriptor, context: BindingContext): Boolean {
|
||||
if (this == null) return false
|
||||
var used = false
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// FIX: Introduce anonymous parameter
|
||||
|
||||
fun test(list: List<String>) {
|
||||
list.for<caret>Each {}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// FIX: Introduce anonymous parameter
|
||||
|
||||
fun test(list: List<String>) {
|
||||
list.forEach { _ -> }
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// WITH_RUNTIME
|
||||
// FIX: Replace with 'repeat(size)'
|
||||
|
||||
fun test(list: List<String>) {
|
||||
list.for<caret>Each {}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// WITH_RUNTIME
|
||||
// FIX: Replace with 'repeat(size)'
|
||||
|
||||
fun test(list: List<String>) {
|
||||
repeat(list.size) {}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun test(list: List<String>?) {
|
||||
list?.for<caret>Each {}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun test(list: List<String>?) {
|
||||
list?.forEach { _ -> }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// WITH_RUNTIME
|
||||
// FIX: Replace with 'repeat(size)'
|
||||
|
||||
fun test(sequence: Sequence<String>) {
|
||||
sequence.for<caret>Each {}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// WITH_RUNTIME
|
||||
// FIX: Replace with 'repeat(size)'
|
||||
|
||||
fun test(sequence: Sequence<String>) {
|
||||
repeat(sequence.count()) {}
|
||||
}
|
||||
+15
@@ -2542,6 +2542,21 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
public void testNormal() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/forEachParameterNotUsed/normal.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("repeat.kt")
|
||||
public void testRepeat() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/forEachParameterNotUsed/repeat.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("safe.kt")
|
||||
public void testSafe() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/forEachParameterNotUsed/safe.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("sequence.kt")
|
||||
public void testSequence() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/forEachParameterNotUsed/sequence.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/inspectionsLocal/ImplicitNullableNothingType")
|
||||
|
||||
Reference in New Issue
Block a user