For each parameter unused: add quick-fix "introduce anonymous parameter"

Related to KT-22068
This commit is contained in:
Mikhail Glukhikh
2018-08-09 17:01:58 +03:00
parent af17a4e961
commit 911f16845e
5 changed files with 58 additions and 16 deletions
@@ -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() {
@@ -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)
}
}
@@ -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 { _ -> }
}
@@ -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")