IDEA-164277 "Simplifiable conditional usage of Map.get()" may suggest putIfAbsent() in following case

This commit is contained in:
Tagir Valeev
2016-11-23 13:14:50 +07:00
parent 42d3290750
commit 7e89cd1ef2
5 changed files with 97 additions and 30 deletions
@@ -47,6 +47,7 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo
public boolean mySuggestMapGetOrDefault = true;
public boolean mySuggestMapComputeIfAbsent = true;
public boolean mySuggestMapPutIfAbsent = true;
@Nullable
@Override
@@ -54,6 +55,7 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo
MultipleCheckboxOptionsPanel panel = new MultipleCheckboxOptionsPanel(this);
panel.addCheckbox("Suggest conversion to Map.computeIfAbsent", "mySuggestMapComputeIfAbsent");
panel.addCheckbox("Suggest conversion to Map.getOrDefault", "mySuggestMapGetOrDefault");
panel.addCheckbox("Suggest conversion to Map.putIfAbsent", "mySuggestMapPutIfAbsent");
return panel;
}
@@ -87,7 +89,8 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo
*/
if (!mySuggestMapGetOrDefault) return;
if (ExpressionUtils.isSimpleExpression(assignment.getRExpression()) &&
equivalence.expressionsAreEquivalent(assignment.getLExpression(), value)) {
equivalence.expressionsAreEquivalent(assignment.getLExpression(), value) &&
!equivalence.expressionsAreEquivalent(assignment.getRExpression(), value)) {
holder.registerProblem(condition, QuickFixBundle.message("java.8.replace.map.get.inspection.description"),
new ReplaceGetNullCheck("getOrDefault"));
}
@@ -99,13 +102,20 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo
map.put(key, value);
}
*/
if (!mySuggestMapComputeIfAbsent) return;
PsiExpression key = getArguments[0];
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"));
if (lambdaCandidate != null && mySuggestMapComputeIfAbsent) {
holder.registerProblem(condition, QuickFixBundle.message("java.8.replace.map.get.inspection.description"),
new ReplaceGetNullCheck("computeIfAbsent"));
}
if (lambdaCandidate == null && mySuggestMapPutIfAbsent) {
PsiExpression expression = extractPutValue(thenBranch, mapExpression, key);
if(ExpressionUtils.isSimpleExpression(expression) && !equivalence.expressionsAreEquivalent(expression, value)) {
holder.registerProblem(condition, QuickFixBundle.message("java.8.replace.map.get.inspection.description"),
new ReplaceGetNullCheck("putIfAbsent"));
}
}
}
}
};
@@ -116,31 +126,18 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo
PsiExpression keyExpression, PsiReferenceExpression valueExpression) {
EquivalenceChecker equivalence = EquivalenceChecker.getCanonicalPsiEquivalence();
PsiAssignmentExpression assignment;
PsiMethodCallExpression putCall = extractPutCall(statement);
if(putCall != null) {
PsiExpression putValue = extractPutValue(statement, mapExpression, keyExpression);
if(putValue != 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]);
assignment = ExpressionUtils.getAssignment(putValue);
}
else {
if (!(statement instanceof PsiBlockStatement)) return null;
// like val = new ArrayList<>(); map.put(key, val);
PsiStatement[] statements = ((PsiBlockStatement)statement).getCodeBlock().getStatements();
if (statements.length != 2) return null;
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;
}
putValue = extractPutValue(statements[1], mapExpression, keyExpression);
if (!equivalence.expressionsAreEquivalent(valueExpression, putValue)) return null;
assignment = ExpressionUtils.getAssignment(statements[0]);
}
if (assignment == null) return null;
@@ -161,6 +158,18 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo
return putCall;
}
@Nullable
private static PsiExpression extractPutValue(PsiStatement statement, PsiExpression mapExpression, PsiExpression keyExpression) {
PsiMethodCallExpression putCall = extractPutCall(statement);
if (putCall == null) return null;
PsiExpression[] putArguments = putCall.getArgumentList().getExpressions();
EquivalenceChecker equivalence = EquivalenceChecker.getCanonicalPsiEquivalence();
return putArguments.length == 2 &&
equivalence.expressionsAreEquivalent(putCall.getMethodExpression().getQualifierExpression(), mapExpression) &&
equivalence.expressionsAreEquivalent(keyExpression, putArguments[0]) ? putArguments[1] : null;
}
@Contract("null -> null")
@Nullable
private static PsiReferenceExpression getReferenceComparedWithNull(PsiExpression condition) {
if(!(condition instanceof PsiBinaryExpression)) return null;
@@ -246,15 +255,22 @@ public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTo
if(assignment != null) {
PsiExpression defaultValue = assignment.getRExpression();
if (!ExpressionUtils.isSimpleExpression(defaultValue)) return;
methodExpression.handleElementRename("getOrDefault");
methodExpression.handleElementRename(myMethodName);
getCall.getArgumentList().add(ct.markUnchanged(defaultValue));
} else {
PsiExpression lambdaCandidate = extractLambdaCandidate(thenBranch, methodExpression.getQualifierExpression(), args[0], value);
if (lambdaCandidate == null) return;
methodExpression.handleElementRename("computeIfAbsent");
String varName = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName("k", lambdaCandidate, true);
PsiExpression lambda = factory.createExpressionFromText(varName + " -> " + ct.text(lambdaCandidate), lambdaCandidate);
getCall.getArgumentList().add(lambda);
if (lambdaCandidate == null) {
PsiExpression valueExpression = extractPutValue(thenBranch, methodExpression.getQualifierExpression(), args[0]);
if(ExpressionUtils.isSimpleExpression(valueExpression)) {
methodExpression.handleElementRename(myMethodName);
getCall.getArgumentList().add(ct.markUnchanged(valueExpression));
}
} else {
methodExpression.handleElementRename(myMethodName);
String varName = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName("k", lambdaCandidate, true);
PsiExpression lambda = factory.createExpressionFromText(varName + " -> " + ct.text(lambdaCandidate), lambdaCandidate);
getCall.getArgumentList().add(lambda);
}
}
ct.deleteAndRestoreComments(ifStatement);
CodeStyleManager.getInstance(project).reformat(statement);
@@ -0,0 +1,12 @@
// "Replace with 'putIfAbsent' method call" "true"
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, String> vals = new HashMap<>();
String v = vals.putIfAbsent("foo", "bar");
System.out.println(v);
}
}
@@ -0,0 +1,15 @@
// "Replace with 'putIfAbsent' method call" "true"
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, String> vals = new HashMap<>();
String v = vals.get("foo");
if(v <caret>== null) {
vals.put("foo", "bar");
}
System.out.println(v);
}
}
@@ -0,0 +1,15 @@
// "Replace with 'putIfAbsent' method call" "false"
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, String> vals = new HashMap<>();
String v = vals.get("foo");
if(v <caret>== null) {
vals.put("foo", v);
}
System.out.println(v);
}
}
@@ -1,6 +1,7 @@
<html>
<body>
Inspection detects calls to <code>Map.get</code> which could be replaced with <code>getOrDefault</code> or <code>computeIfAbsent</code>.
Inspection detects calls to <code>Map.get</code> which could be replaced with <code>getOrDefault</code>, <code>computeIfAbsent</code> or
<code>putIfAbsent</code>.
<!-- tooltip end -->
<ul>
<li><b>Map.getOrDefault</b> method could be used to replace the code like this:
@@ -20,6 +21,14 @@ Inspection detects calls to <code>Map.get</code> which could be replaced with <c
}
</pre>
</li>
<li><b>Map.putIfAbsent</b> method could be used to replace the code like this:
<pre>
String val = map.get(key);
if (val == null) {
map.put(key, newVal);
}
</pre>
</li>
</ul>
<p>This conversion is available since Java 8 only.</p>
<small>New in 2016.3</small>