diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/lambdaToExplicit/ExcessiveLambdaUsageInspection.java b/java/java-impl-inspections/src/com/intellij/codeInspection/lambdaToExplicit/ExcessiveLambdaUsageInspection.java index 67f19b54cff4..958cba22e5e6 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/lambdaToExplicit/ExcessiveLambdaUsageInspection.java +++ b/java/java-impl-inspections/src/com/intellij/codeInspection/lambdaToExplicit/ExcessiveLambdaUsageInspection.java @@ -6,16 +6,23 @@ import com.intellij.java.JavaBundle; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; +import com.siyeh.ig.callMatcher.CallMatcher; import com.siyeh.ig.psiutils.CommentTracker; import com.siyeh.ig.psiutils.ExpressionUtils; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.siyeh.ig.callMatcher.CallMatcher.instanceCall; public class ExcessiveLambdaUsageInspection extends AbstractBaseJavaLocalInspectionTool { + private static final CallMatcher LIST_REPLACE_ALL = + instanceCall(CommonClassNames.JAVA_UTIL_LIST, "replaceAll").parameterTypes("java.util.function.UnaryOperator"); @NotNull @Override @@ -34,17 +41,26 @@ public class ExcessiveLambdaUsageInspection extends AbstractBaseJavaLocalInspect PsiExpression expr = (PsiExpression)lambda.getBody(); if (!ExpressionUtils.isSafelyRecomputableExpression(expr)) return; if (ContainerUtil.or(lambda.getParameterList().getParameters(), - param -> ExpressionUtils.isReferenceTo(expr, param))) return; - + param -> ExpressionUtils.isReferenceTo(expr, param))) { + return; + } + if (LIST_REPLACE_ALL.test(call)) { + registerProblem(lambda, expr, new ReplaceWithCollectionsFillFix()); + return; + } for (LambdaAndExplicitMethodPair info : LambdaAndExplicitMethodPair.INFOS) { - if(info.isLambdaCall(call, lambda)) { - holder.registerProblem(lambda, JavaBundle.message("inspection.excessive.lambda.message"), - ProblemHighlightType.LIKE_UNUSED_SYMBOL, - new TextRange(0, expr.getStartOffsetInParent()), - new RemoveExcessiveLambdaFix(info, info.getExplicitMethodName(call))); + if (info.isLambdaCall(call, lambda)) { + registerProblem(lambda, expr, new RemoveExcessiveLambdaFix(info, info.getExplicitMethodName(call))); } } } + + private void registerProblem(PsiLambdaExpression lambda, PsiExpression expr, LocalQuickFix fix) { + holder.registerProblem(lambda, JavaBundle.message("inspection.excessive.lambda.message"), + ProblemHighlightType.LIKE_UNUSED_SYMBOL, + new TextRange(0, expr.getStartOffsetInParent()), + fix); + } }; } @@ -73,17 +89,58 @@ public class ExcessiveLambdaUsageInspection extends AbstractBaseJavaLocalInspect @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiElement element = descriptor.getStartElement(); - if(!(element instanceof PsiLambdaExpression)) return; + Context context = Context.from(descriptor.getStartElement()); + if (context == null) return; + ExpressionUtils.bindCallTo(context.myCall, myInfo.getExplicitMethodName(context.myCall)); + CommentTracker ct = new CommentTracker(); + ct.replaceAndRestoreComments(context.myLambda, ct.text(context.myBody)); + } + } + + static class ReplaceWithCollectionsFillFix implements LocalQuickFix { + @Override + public @NotNull String getFamilyName() { + return JavaBundle.message("inspection.excessive.lambda.fix.name", "Collections.fill()"); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + Context context = Context.from(descriptor.getStartElement()); + if (context == null) return; + PsiExpression expression = ExpressionUtils.getEffectiveQualifier(context.myCall.getMethodExpression()); + if (expression == null) return; + CommentTracker ct = new CommentTracker(); + String firstArg = expression instanceof PsiSuperExpression ? "this" : ct.text(expression); + PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); + String text = "java.util.Collections.fill(" + firstArg + ", " + ct.text(context.myBody) + ")"; + PsiExpression replacement = factory.createExpressionFromText(text, context.myCall); + JavaCodeStyleManager.getInstance(project).shortenClassReferences(replacement); + ct.replaceAndRestoreComments(context.myCall, replacement); + } + } + + private static class Context { + private @NotNull final PsiMethodCallExpression myCall; + private @NotNull final PsiLambdaExpression myLambda; + private @NotNull final PsiElement myBody; + + private Context(@NotNull PsiMethodCallExpression call, + @NotNull PsiLambdaExpression lambda, + @NotNull PsiElement body) { + myCall = call; + myLambda = lambda; + myBody = body; + } + + @Nullable + static Context from(@Nullable PsiElement element) { + if (!(element instanceof PsiLambdaExpression)) return null; PsiLambdaExpression lambda = (PsiLambdaExpression)element; PsiElement body = lambda.getBody(); - if(body == null) return; + if (body == null) return null; PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(lambda, PsiMethodCallExpression.class); - if(call == null) return; - - ExpressionUtils.bindCallTo(call, myInfo.getExplicitMethodName(call)); - CommentTracker ct = new CommentTracker(); - ct.replaceAndRestoreComments(lambda, ct.text(body)); + if (call == null) return null; + return new Context(call, lambda, body); } } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/afterListReplaceAll.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/afterListReplaceAll.java new file mode 100644 index 000000000000..01c74aab8a0f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/afterListReplaceAll.java @@ -0,0 +1,11 @@ +// "Fix all 'Excessive lambda usage' problems in file" "true" +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +class Test extends ArrayList { + public void test(List list, String replacement) { + Collections.fill(list, replacement); + Collections.fill(this, replacement); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/beforeListReplaceAll.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/beforeListReplaceAll.java new file mode 100644 index 000000000000..36066146e3a1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/excessiveLambdaUsage/beforeListReplaceAll.java @@ -0,0 +1,10 @@ +// "Fix all 'Excessive lambda usage' problems in file" "true" +import java.util.ArrayList; +import java.util.List; + +class Test extends ArrayList { + public void test(List list, String replacement) { + list.replaceAll(ignored -> replacement); + super.replaceAll(ignored -> replacement); + } +} \ No newline at end of file