IDEA-161257 Follow-up: support scenarios like map.put(key, value = new ArrayList<>()); refactor

This commit is contained in:
Tagir Valeev
2016-10-12 14:25:24 +03:00
parent bab9245f27
commit 73eef6c8ac
4 changed files with 103 additions and 48 deletions
@@ -15,15 +15,14 @@
*/
package com.intellij.codeInspection.java18api;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.codeInspection.util.LambdaGenerationUtil;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
@@ -93,7 +92,7 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo
holder.registerProblem(condition, QuickFixBundle.message("java.8.replace.map.get.inspection.description"),
new ReplaceGetNullCheck("getOrDefault"));
}
} else if(thenBranch instanceof PsiBlockStatement) {
} else {
/*
value = map.get(key);
if(value == null) {
@@ -103,37 +102,9 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo
*/
if (!mySuggestMapComputeIfAbsent) return;
PsiExpression key = getArguments[0];
PsiStatement[] statements = ((PsiBlockStatement)thenBranch).getCodeBlock().getStatements();
if(statements.length != 2) return;
assignment = ExpressionUtils.getAssignment(statements[0]);
if(assignment == null) return;
PsiExpression lambdaCandidate = assignment.getRExpression();
if (lambdaCandidate == null ||
!equivalence.expressionsAreEquivalent(assignment.getLExpression(), value) ||
!(statements[1] instanceof PsiExpressionStatement)) {
return;
}
PsiExpression expression = ((PsiExpressionStatement)statements[1]).getExpression();
if(!(expression instanceof PsiMethodCallExpression)) return;
PsiMethodCallExpression putCall = (PsiMethodCallExpression)expression;
if(!Java8CollectionsApiInspection.isJavaUtilMapMethodWithName(putCall, "put")) return;
PsiExpression[] putArguments = putCall.getArgumentList().getExpressions();
if (putArguments.length != 2 ||
!equivalence.expressionsAreEquivalent(putCall.getMethodExpression().getQualifierExpression(),
getCall.getMethodExpression().getQualifierExpression()) ||
!equivalence.expressionsAreEquivalent(key, putArguments[0]) ||
!equivalence.expressionsAreEquivalent(value, putArguments[1])) {
return;
}
if(!ExceptionUtil.getThrownCheckedExceptions(lambdaCandidate).isEmpty()) return;
if(!PsiTreeUtil.processElements(lambdaCandidate, e -> {
if(!(e instanceof PsiReferenceExpression)) return true;
PsiElement element = ((PsiReferenceExpression)e).resolve();
if(!(element instanceof PsiVariable)) return true;
return HighlightControlFlowUtil.isEffectivelyFinal((PsiVariable)element, lambdaCandidate, null);
})) {
return;
}
PsiExpression mapExpression = getCall.getMethodExpression().getQualifierExpression();
PsiExpression lambdaCandidate = extractLambdaCandidate(thenBranch, mapExpression, key, value);
if (lambdaCandidate == null) return;
holder.registerProblem(condition, QuickFixBundle.message("java.8.replace.map.get.inspection.description"),
new ReplaceGetNullCheck("computeIfAbsent"));
}
@@ -141,6 +112,55 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo
};
}
@Nullable
static PsiExpression extractLambdaCandidate(PsiStatement statement, PsiExpression mapExpression,
PsiExpression keyExpression, PsiReferenceExpression valueExpression) {
EquivalenceChecker equivalence = EquivalenceChecker.getCanonicalPsiEquivalence();
PsiAssignmentExpression assignment;
PsiMethodCallExpression putCall = extractPutCall(statement);
if(putCall != null) {
// like map.put(key, val = new ArrayList<>());
PsiExpression[] putArguments = putCall.getArgumentList().getExpressions();
if (putArguments.length != 2 ||
!equivalence.expressionsAreEquivalent(putCall.getMethodExpression().getQualifierExpression(), mapExpression) ||
!equivalence.expressionsAreEquivalent(keyExpression, putArguments[0])) {
return null;
}
assignment = ExpressionUtils.getAssignment(putArguments[1]);
}
else {
if (!(statement instanceof PsiBlockStatement)) return null;
// like val = new ArrayList<>(); map.put(key, val);
PsiStatement[] statements = ((PsiBlockStatement)statement).getCodeBlock().getStatements();
putCall = extractPutCall(statements[1]);
if (putCall == null) return null;
PsiExpression[] putArguments = putCall.getArgumentList().getExpressions();
if (putArguments.length != 2 ||
!equivalence.expressionsAreEquivalent(putCall.getMethodExpression().getQualifierExpression(), mapExpression) ||
!equivalence.expressionsAreEquivalent(keyExpression, putArguments[0]) ||
!equivalence.expressionsAreEquivalent(valueExpression, putArguments[1])) {
return null;
}
assignment = ExpressionUtils.getAssignment(statements[0]);
}
if (assignment == null) return null;
PsiExpression lambdaCandidate = assignment.getRExpression();
if (lambdaCandidate == null || !equivalence.expressionsAreEquivalent(assignment.getLExpression(), valueExpression)) return null;
if (!LambdaGenerationUtil.canBeUncheckedLambda(lambdaCandidate)) return null;
return lambdaCandidate;
}
@Contract("null -> null")
@Nullable
private static PsiMethodCallExpression extractPutCall(PsiStatement statement) {
if(!(statement instanceof PsiExpressionStatement)) return null;
PsiExpression expression = ((PsiExpressionStatement)statement).getExpression();
if (!(expression instanceof PsiMethodCallExpression)) return null;
PsiMethodCallExpression putCall = (PsiMethodCallExpression)expression;
if (!Java8CollectionsApiInspection.isJavaUtilMapMethodWithName(putCall, "put")) return null;
return putCall;
}
@Nullable
private static PsiReferenceExpression getReferenceComparedWithNull(PsiExpression condition) {
if(!(condition instanceof PsiBinaryExpression)) return null;
@@ -223,27 +243,22 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo
Collection<PsiComment> comments = ContainerUtil.map(PsiTreeUtil.findChildrenOfType(ifStatement, PsiComment.class),
comment -> (PsiComment)comment.copy());
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
if(thenBranch instanceof PsiExpressionStatement) {
PsiExpression expression = ((PsiExpressionStatement)thenBranch).getExpression();
if (!(expression instanceof PsiAssignmentExpression)) return;
PsiExpression defaultValue = ((PsiAssignmentExpression)expression).getRExpression();
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(thenBranch);
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
if(assignment != null) {
PsiExpression defaultValue = assignment.getRExpression();
if (!ExpressionUtils.isSimpleExpression(defaultValue)) return;
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
nameElement.replace(factory.createIdentifier("getOrDefault"));
getCall.getArgumentList().add(defaultValue);
} else if(thenBranch instanceof PsiBlockStatement) {
PsiStatement[] statements = ((PsiBlockStatement)thenBranch).getCodeBlock().getStatements();
if(statements.length != 2) return;
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(statements[0]);
if(assignment == null) return;
PsiExpression lambdaCandidate = assignment.getRExpression();
if(lambdaCandidate == null) return;
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
} else {
PsiExpression lambdaCandidate =
extractLambdaCandidate(thenBranch, getCall.getMethodExpression().getQualifierExpression(), args[0], value);
if (lambdaCandidate == null) return;
nameElement.replace(factory.createIdentifier("computeIfAbsent"));
String varName = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName("k", lambdaCandidate, true);
PsiExpression lambda = factory.createExpressionFromText(varName + " -> " + lambdaCandidate.getText(), lambdaCandidate);
getCall.getArgumentList().add(lambda);
} else return;
}
ifStatement.delete();
CodeStyleManager.getInstance(project).reformat(statement);
comments.forEach(comment -> statement.getParent().addBefore(comment, statement));
@@ -0,0 +1,11 @@
// "Replace with 'computeIfAbsent' method call" "true"
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Main {
public void testMap(Map<String, List<String>> map, String key, String value) {
List<String> list = map.computeIfAbsent(key, k -> new ArrayList<>());
list.add(value);
}
}
@@ -0,0 +1,14 @@
// "Replace with 'computeIfAbsent' method call" "true"
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Main {
public void testMap(Map<String, List<String>> map, String key, String value) {
List<String> list = map.get(key);
if(list == nul<caret>l) {
map.put(key, list = new ArrayList<>());
}
list.add(value);
}
}
@@ -0,0 +1,15 @@
// "Replace with 'computeIfAbsent' method call" "false"
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class Main {
public void testMap(Map<String, List<String>> map, String key, String value) {
List<String> list2 = null;
List<String> list = map.get(key);
if(list == nul<caret>l) {
map.put(key, list2 = new ArrayList<>());
}
list.add(value);
}
}