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.codeInspection.ProblemsHolder
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiElementVisitor
|
import com.intellij.psi.PsiElementVisitor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl.WithDestructuringDeclaration
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl.WithDestructuringDeclaration
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
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.intentions.SpecifyExplicitLambdaSignatureIntention
|
||||||
|
import org.jetbrains.kotlin.idea.project.builtIns
|
||||||
import org.jetbrains.kotlin.idea.refactoring.getThisLabelName
|
import org.jetbrains.kotlin.idea.refactoring.getThisLabelName
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
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.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||||
@@ -45,11 +51,16 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() {
|
|||||||
if (iterableParameter !is WithDestructuringDeclaration &&
|
if (iterableParameter !is WithDestructuringDeclaration &&
|
||||||
!lambda.bodyExpression.usesDescriptor(iterableParameter, context)
|
!lambda.bodyExpression.usesDescriptor(iterableParameter, context)
|
||||||
) {
|
) {
|
||||||
|
val fixes = mutableListOf<LocalQuickFix>()
|
||||||
|
if (it.parent is KtDotQualifiedExpression) {
|
||||||
|
fixes += ReplaceWithRepeatFix()
|
||||||
|
}
|
||||||
|
fixes += IntroduceAnonymousParameterFix()
|
||||||
holder.registerProblem(
|
holder.registerProblem(
|
||||||
calleeExpression,
|
calleeExpression,
|
||||||
"Loop parameter '${iterableParameter.getThisLabelName()}' is unused",
|
"Loop parameter '${iterableParameter.getThisLabelName()}' is unused",
|
||||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
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 {
|
private fun KtBlockExpression?.usesDescriptor(descriptor: VariableDescriptor, context: BindingContext): Boolean {
|
||||||
if (this == null) return false
|
if (this == null) return false
|
||||||
var used = false
|
var used = false
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// FIX: Introduce anonymous parameter
|
||||||
|
|
||||||
fun test(list: List<String>) {
|
fun test(list: List<String>) {
|
||||||
list.for<caret>Each {}
|
list.for<caret>Each {}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// FIX: Introduce anonymous parameter
|
||||||
|
|
||||||
fun test(list: List<String>) {
|
fun test(list: List<String>) {
|
||||||
list.forEach { _ -> }
|
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 {
|
public void testNormal() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/forEachParameterNotUsed/normal.kt");
|
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")
|
@TestMetadata("idea/testData/inspectionsLocal/ImplicitNullableNothingType")
|
||||||
|
|||||||
Reference in New Issue
Block a user