diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ForEachParameterNotUsedInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ForEachParameterNotUsedInspection.kt index e9c88361439..9968135676f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ForEachParameterNotUsedInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ForEachParameterNotUsedInspection.kt @@ -5,13 +5,17 @@ package org.jetbrains.kotlin.idea.inspections +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor 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.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.intentions.SpecifyExplicitLambdaSignatureIntention import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor import org.jetbrains.kotlin.idea.refactoring.getThisLabelName import org.jetbrains.kotlin.name.FqName @@ -41,11 +45,11 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() { if (iterableParameter !is WithDestructuringDeclaration && lambda.bodyExpression?.usesDescriptor(iterableParameter) != true ) { - holder.registerProblem( calleeExpression, "Loop parameter '${iterableParameter.getThisLabelName()}' is unused", - ProblemHighlightType.GENERIC_ERROR_OR_WARNING + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + IntroduceAnonymousParameterFix() ) } } @@ -53,6 +57,20 @@ class ForEachParameterNotUsedInspection : AbstractKotlinInspection() { } } + private class IntroduceAnonymousParameterFix : LocalQuickFix { + override fun getFamilyName() = "Introduce anonymous parameter" + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val callExpression = descriptor.psiElement.parent as? KtCallExpression ?: return + val literal = callExpression.lambdaArguments.singleOrNull()?.getLambdaExpression()?.functionLiteral ?: return + val psiFactory = KtPsiFactory(project) + val newParameterList = psiFactory.createLambdaParameterList("_") + with (SpecifyExplicitLambdaSignatureIntention) { + literal.setParameterListIfAny(psiFactory, newParameterList) + } + } + } + private fun KtBlockExpression.usesDescriptor(descriptor: VariableDescriptor): Boolean { var used = false acceptChildren(object : KtVisitorVoid() { diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyExplicitLambdaSignatureIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyExplicitLambdaSignatureIntention.kt index f398cfcd37e..776ec330407 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyExplicitLambdaSignatureIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyExplicitLambdaSignatureIntention.kt @@ -23,7 +23,9 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.psi.KtFunctionLiteral import org.jetbrains.kotlin.psi.KtLambdaExpression +import org.jetbrains.kotlin.psi.KtParameterList import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode @@ -59,24 +61,31 @@ class SpecifyExplicitLambdaSignatureIntention : SelfTargetingOffsetIndependentIn } companion object { + + fun KtFunctionLiteral.setParameterListIfAny(psiFactory: KtPsiFactory, newParameterList: KtParameterList?) { + val oldParameterList = valueParameterList + if (oldParameterList != null && newParameterList != null) { + oldParameterList.replace(newParameterList) + } else { + val openBraceElement = lBrace + val nextSibling = openBraceElement.nextSibling + val addNewline = nextSibling is PsiWhiteSpace && nextSibling.text?.contains("\n") ?: false + val (whitespace, arrow) = psiFactory.createWhitespaceAndArrow() + addRangeAfter(whitespace, arrow, openBraceElement) + if (newParameterList != null) { + addAfter(newParameterList, openBraceElement) + } + if (addNewline) { + addAfter(psiFactory.createNewLine(), openBraceElement) + } + } + } + fun applyWithParameters(element: KtLambdaExpression, parameterString: String) { val psiFactory = KtPsiFactory(element) val functionLiteral = element.functionLiteral val newParameterList = psiFactory.createLambdaParameterListIfAny(parameterString) - val oldParameterList = functionLiteral.valueParameterList - if (oldParameterList != null && newParameterList != null) { - oldParameterList.replace(newParameterList) - } else { - val openBraceElement = functionLiteral.lBrace - val nextSibling = openBraceElement.nextSibling - val addNewline = nextSibling is PsiWhiteSpace && nextSibling.text?.contains("\n") ?: false - val (whitespace, arrow) = psiFactory.createWhitespaceAndArrow() - functionLiteral.addRangeAfter(whitespace, arrow, openBraceElement) - newParameterList?.let { functionLiteral.addAfter(it, openBraceElement) } - if (addNewline) { - functionLiteral.addAfter(psiFactory.createNewLine(), openBraceElement) - } - } + functionLiteral.setParameterListIfAny(psiFactory, newParameterList) ShortenReferences.DEFAULT.process(element.valueParameters) } } diff --git a/idea/testData/inspectionsLocal/forEachParameterNotUsed/normal.kt b/idea/testData/inspectionsLocal/forEachParameterNotUsed/normal.kt new file mode 100644 index 00000000000..c4da8fc0000 --- /dev/null +++ b/idea/testData/inspectionsLocal/forEachParameterNotUsed/normal.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun test(list: List) { + list.forEach {} +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/forEachParameterNotUsed/normal.kt.after b/idea/testData/inspectionsLocal/forEachParameterNotUsed/normal.kt.after new file mode 100644 index 00000000000..f4716c40756 --- /dev/null +++ b/idea/testData/inspectionsLocal/forEachParameterNotUsed/normal.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun test(list: List) { + list.forEach { _ -> } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 0a5d8a0457d..c8c575bf313 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -2177,6 +2177,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { public void testInvoke() throws Exception { runTest("idea/testData/inspectionsLocal/forEachParameterNotUsed/invoke.kt"); } + + @TestMetadata("normal.kt") + public void testNormal() throws Exception { + runTest("idea/testData/inspectionsLocal/forEachParameterNotUsed/normal.kt"); + } } @TestMetadata("idea/testData/inspectionsLocal/ImplicitNullableNothingType")