[java-inspections] ExcessiveLambdaUsage: suggest replacing 'list.replaceAll(… -> …)' with 'Collections.fill(list, …)'

GitOrigin-RevId: 5a548eb7a9e9d78ca46188b29c3e69f2502b99d3
This commit is contained in:
Andrey.Cherkasov
2021-12-31 16:26:05 +00:00
committed by intellij-monorepo-bot
parent 8a64253e8d
commit 8bd14ace33
3 changed files with 93 additions and 15 deletions
@@ -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);
}
}
}
@@ -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<String> {
public void test(List<String> list, String replacement) {
Collections.fill(list, replacement);
Collections.fill(this, replacement);
}
}
@@ -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<String> {
public void test(List<String> list, String replacement) {
list.replaceAll(ignored <caret>-> replacement);
super.replaceAll(ignored -> replacement);
}
}