mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-13 09:19:13 +07:00
Java8CollectionsApiInspection & Java8ReplaceMapGetInspection merged into Java8MapApiInspection; support method references in computeIfAbsent
fix for IDEA-163932 Map.get suggested replacement with Map.getOrDefault is misleading
This commit is contained in:
-296
@@ -1,296 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection.java18api;
|
||||
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
|
||||
import com.intellij.codeInspection.util.LambdaGenerationUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.ig.psiutils.EquivalenceChecker;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
private static final Logger LOG = Logger.getInstance(Java8CollectionsApiInspection.class);
|
||||
|
||||
public boolean myReportContainsCondition;
|
||||
public boolean mySuggestPutIfAbsentForComplexExpression;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
MultipleCheckboxOptionsPanel panel = new MultipleCheckboxOptionsPanel(this);
|
||||
panel.addCheckbox("Report when \'containsKey\' is used in condition (may change semantics)", "myReportContainsCondition");
|
||||
panel.addCheckbox("Suggest to replace with \'putIfAbsent\' if value is complex expression (may change semantics)", "mySuggestPutIfAbsentForComplexExpression");
|
||||
return panel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
|
||||
if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) {
|
||||
return PsiElementVisitor.EMPTY_VISITOR;
|
||||
}
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitConditionalExpression(PsiConditionalExpression expression) {
|
||||
final ConditionInfo conditionInfo = extractConditionInfo(expression.getCondition());
|
||||
if (conditionInfo == null) return;
|
||||
final PsiExpression thenExpression = expression.getThenExpression();
|
||||
final PsiExpression elseExpression = expression.getElseExpression();
|
||||
if (thenExpression == null || elseExpression == null) return;
|
||||
analyzeCorrespondenceOfPutAndGet(conditionInfo.isInverted() ? thenExpression : elseExpression,
|
||||
conditionInfo.isInverted() ? elseExpression : thenExpression,
|
||||
conditionInfo.getQualifier(), conditionInfo.getContainsKey(),
|
||||
holder, expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIfStatement(PsiIfStatement statement) {
|
||||
final PsiExpression condition = statement.getCondition();
|
||||
final ConditionInfo conditionInfo = extractConditionInfo(condition);
|
||||
if (conditionInfo == null) return;
|
||||
PsiStatement maybeGetBranch = conditionInfo.isInverted() ? statement.getElseBranch() : statement.getThenBranch();
|
||||
if (maybeGetBranch instanceof PsiBlockStatement) {
|
||||
final PsiStatement[] getBranchStatements = ((PsiBlockStatement)maybeGetBranch).getCodeBlock().getStatements();
|
||||
if (getBranchStatements.length > 1) return;
|
||||
maybeGetBranch = getBranchStatements.length == 0 ? null : getBranchStatements[0];
|
||||
}
|
||||
final PsiStatement branch = conditionInfo.isInverted() ? statement.getThenBranch() : statement.getElseBranch();
|
||||
final PsiStatement maybePutStatement;
|
||||
if (branch instanceof PsiBlockStatement) {
|
||||
final PsiStatement[] statements = ((PsiBlockStatement)branch).getCodeBlock().getStatements();
|
||||
if (statements.length != 1) return;
|
||||
maybePutStatement = statements[statements.length - 1];
|
||||
}
|
||||
else {
|
||||
maybePutStatement = branch;
|
||||
}
|
||||
if (maybePutStatement != null) {
|
||||
analyzeCorrespondenceOfPutAndGet(maybePutStatement, maybeGetBranch, conditionInfo.getQualifier(), conditionInfo.getContainsKey(),
|
||||
holder, statement);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ConditionInfo extractConditionInfo(PsiExpression condition) {
|
||||
final ConditionInfo info = extractConditionInfoIfGet(condition);
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
return !myReportContainsCondition ? null : extractConditionInfoIfContains(condition);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static PsiExpression getValueComparedWithNull(PsiBinaryExpression binOp) {
|
||||
if(!binOp.getOperationTokenType().equals(JavaTokenType.EQEQ) &&
|
||||
!binOp.getOperationTokenType().equals(JavaTokenType.NE)) return null;
|
||||
PsiExpression left = binOp.getLOperand();
|
||||
PsiExpression right = binOp.getROperand();
|
||||
if(ExpressionUtils.isNullLiteral(right)) return left;
|
||||
if(ExpressionUtils.isNullLiteral(left)) return right;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ConditionInfo extractConditionInfoIfGet(PsiExpression condition) {
|
||||
if(!(condition instanceof PsiBinaryExpression)) return null;
|
||||
PsiBinaryExpression binOp = (PsiBinaryExpression)condition;
|
||||
PsiExpression operand = getValueComparedWithNull(binOp);
|
||||
if (!(operand instanceof PsiMethodCallExpression)) return null;
|
||||
final PsiMethodCallExpression maybeGetCall = (PsiMethodCallExpression)operand;
|
||||
if (!isJavaUtilMapMethodWithName(maybeGetCall, "get")) return null;
|
||||
final PsiExpression[] arguments = maybeGetCall.getArgumentList().getExpressions();
|
||||
if (arguments.length != 1) return null;
|
||||
PsiExpression getQualifier = maybeGetCall.getMethodExpression().getQualifierExpression();
|
||||
PsiExpression keyExpression = arguments[0];
|
||||
return new ConditionInfo(getQualifier, keyExpression, binOp.getOperationTokenType().equals(JavaTokenType.EQEQ));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ConditionInfo extractConditionInfoIfContains(PsiExpression condition) {
|
||||
boolean inverted = false;
|
||||
final PsiMethodCallExpression conditionMethodCall;
|
||||
if (condition instanceof PsiPrefixExpression) {
|
||||
final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)condition;
|
||||
if (JavaTokenType.EXCL.equals(prefixExpression.getOperationSign().getTokenType()) &&
|
||||
prefixExpression.getOperand() instanceof PsiMethodCallExpression) {
|
||||
conditionMethodCall = (PsiMethodCallExpression)prefixExpression.getOperand();
|
||||
inverted = true;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (condition instanceof PsiMethodCallExpression) {
|
||||
conditionMethodCall = (PsiMethodCallExpression)condition;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
if (!isJavaUtilMapMethodWithName(conditionMethodCall, "containsKey")) {
|
||||
return null;
|
||||
}
|
||||
final PsiExpression containsQualifier = conditionMethodCall.getMethodExpression().getQualifierExpression();
|
||||
if (containsQualifier == null) {
|
||||
return null;
|
||||
}
|
||||
final PsiExpression[] expressions = conditionMethodCall.getArgumentList().getExpressions();
|
||||
if (expressions.length != 1) {
|
||||
return null;
|
||||
}
|
||||
PsiExpression containsKey = expressions[0];
|
||||
return new ConditionInfo(containsQualifier, containsKey, inverted);
|
||||
}
|
||||
|
||||
private void analyzeCorrespondenceOfPutAndGet(@NotNull PsiElement adjustedElseBranch,
|
||||
@Nullable PsiElement adjustedThenBranch,
|
||||
@Nullable PsiExpression containsQualifier,
|
||||
@Nullable PsiExpression containsKey,
|
||||
@NotNull ProblemsHolder holder,
|
||||
@NotNull PsiElement context) {
|
||||
final PsiElement maybePutMethodCall;
|
||||
final PsiElement maybeGetMethodCall;
|
||||
if (adjustedThenBranch == null) {
|
||||
maybeGetMethodCall = null;
|
||||
if (adjustedElseBranch instanceof PsiExpressionStatement) {
|
||||
final PsiExpression expression = ((PsiExpressionStatement)adjustedElseBranch).getExpression();
|
||||
if (expression instanceof PsiMethodCallExpression && isJavaUtilMapMethodWithName((PsiMethodCallExpression)expression, "put")) {
|
||||
maybePutMethodCall = expression;
|
||||
}
|
||||
else {
|
||||
maybePutMethodCall = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
maybePutMethodCall = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (adjustedElseBranch instanceof PsiStatement && adjustedThenBranch instanceof PsiStatement) {
|
||||
final EquivalenceChecker.Decision decision = EquivalenceChecker.getCanonicalPsiEquivalence().statementsAreEquivalentDecision((PsiStatement)adjustedElseBranch,
|
||||
(PsiStatement)adjustedThenBranch);
|
||||
maybePutMethodCall = decision.getLeftDiff();
|
||||
maybeGetMethodCall = decision.getRightDiff();
|
||||
}
|
||||
else {
|
||||
maybePutMethodCall = adjustedElseBranch;
|
||||
maybeGetMethodCall = adjustedThenBranch;
|
||||
}
|
||||
}
|
||||
if (maybePutMethodCall instanceof PsiMethodCallExpression &&
|
||||
(maybeGetMethodCall == null || maybeGetMethodCall instanceof PsiMethodCallExpression)) {
|
||||
final PsiMethodCallExpression putMethodCall = (PsiMethodCallExpression)maybePutMethodCall;
|
||||
final PsiMethodCallExpression getMethodCall = (PsiMethodCallExpression)maybeGetMethodCall;
|
||||
final PsiExpression putQualifier = putMethodCall.getMethodExpression().getQualifierExpression();
|
||||
final PsiExpression getQualifier = getMethodCall == null ? null : getMethodCall.getMethodExpression().getQualifierExpression();
|
||||
if ((getMethodCall == null || EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(putQualifier, getQualifier)) &&
|
||||
EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(putQualifier, containsQualifier) &&
|
||||
isJavaUtilMapMethodWithName(putMethodCall, "put") &&
|
||||
(getMethodCall == null || isJavaUtilMapMethodWithName(getMethodCall, "get"))) {
|
||||
|
||||
PsiExpression getArgument;
|
||||
if (getMethodCall != null) {
|
||||
final PsiExpression[] arguments = getMethodCall.getArgumentList().getExpressions();
|
||||
if (arguments.length != 1) {
|
||||
return;
|
||||
}
|
||||
getArgument = arguments[0];
|
||||
}
|
||||
else {
|
||||
getArgument = null;
|
||||
}
|
||||
|
||||
final PsiExpression[] putArguments = putMethodCall.getArgumentList().getExpressions();
|
||||
if (putArguments.length != 2) {
|
||||
return;
|
||||
}
|
||||
PsiExpression putKeyArgument = putArguments[0];
|
||||
PsiExpression putValueArgument = putArguments[1];
|
||||
|
||||
if (EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(containsKey, putKeyArgument) &&
|
||||
(getArgument == null || EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(getArgument, putKeyArgument))) {
|
||||
LocalQuickFix fix = null;
|
||||
if (ExpressionUtils.isSimpleExpression(putValueArgument)) {
|
||||
fix = new ReplaceConditionalMapPutFix(putMethodCall, false);
|
||||
}
|
||||
else if ((maybePutMethodCall.getParent() instanceof PsiExpressionStatement) && // only if result of put is not used
|
||||
LambdaGenerationUtil.canBeUncheckedLambda(putValueArgument)) {
|
||||
fix = new ReplaceConditionalMapPutFix(putMethodCall, true);
|
||||
}
|
||||
else if (mySuggestPutIfAbsentForComplexExpression) {
|
||||
fix = new ReplaceConditionalMapPutFix(putMethodCall, false);
|
||||
}
|
||||
if(fix != null) {
|
||||
holder.registerProblem(context, QuickFixBundle.message("java.8.collections.api.inspection.description"), fix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isJavaUtilMapMethodWithName(@NotNull PsiMethodCallExpression methodCallExpression, @NotNull String expectedName) {
|
||||
if (!expectedName.equals(methodCallExpression.getMethodExpression().getReferenceName())) {
|
||||
return false;
|
||||
}
|
||||
final PsiMethod method = methodCallExpression.resolveMethod();
|
||||
if (method == null) return false;
|
||||
PsiMethod[] superMethods = method.findDeepestSuperMethods();
|
||||
if (superMethods.length == 0) {
|
||||
superMethods = new PsiMethod[]{method};
|
||||
}
|
||||
return StreamEx.of(superMethods).map(PsiMember::getContainingClass).nonNull().map(PsiClass::getQualifiedName)
|
||||
.has(CommonClassNames.JAVA_UTIL_MAP);
|
||||
}
|
||||
|
||||
private static class ConditionInfo {
|
||||
private final PsiExpression myQualifier;
|
||||
private final PsiExpression myContainsKey;
|
||||
private final boolean myInverted;
|
||||
|
||||
private ConditionInfo(PsiExpression qualifier, PsiExpression containsKey, boolean inverted) {
|
||||
myQualifier = qualifier;
|
||||
myContainsKey = containsKey;
|
||||
myInverted = inverted;
|
||||
}
|
||||
|
||||
public PsiExpression getQualifier() {
|
||||
return myQualifier;
|
||||
}
|
||||
|
||||
public PsiExpression getContainsKey() {
|
||||
return myContainsKey;
|
||||
}
|
||||
|
||||
public boolean isInverted() {
|
||||
return myInverted;
|
||||
}
|
||||
}
|
||||
}
|
||||
-277
@@ -1,277 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection.java18api;
|
||||
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
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;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.ControlFlowUtils;
|
||||
import com.siyeh.ig.psiutils.EquivalenceChecker;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
|
||||
public boolean mySuggestMapGetOrDefault = true;
|
||||
public boolean mySuggestMapComputeIfAbsent = true;
|
||||
public boolean mySuggestMapPutIfAbsent = true;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
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;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
|
||||
if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) {
|
||||
return PsiElementVisitor.EMPTY_VISITOR;
|
||||
}
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitIfStatement(PsiIfStatement statement) {
|
||||
if(statement.getElseBranch() != null) return;
|
||||
PsiExpression condition = statement.getCondition();
|
||||
PsiReferenceExpression value = getReferenceComparedWithNull(condition);
|
||||
if (value == null) return;
|
||||
PsiElement previous = PsiTreeUtil.skipSiblingsBackward(statement, PsiWhiteSpace.class, PsiComment.class);
|
||||
PsiMethodCallExpression getCall = tryExtractMapGetCall(value, previous);
|
||||
if(getCall == null) return;
|
||||
PsiExpression[] getArguments = getCall.getArgumentList().getExpressions();
|
||||
if(getArguments.length != 1) return;
|
||||
PsiStatement thenBranch = ControlFlowUtils.stripBraces(statement.getThenBranch());
|
||||
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(thenBranch);
|
||||
EquivalenceChecker equivalence = EquivalenceChecker.getCanonicalPsiEquivalence();
|
||||
if(assignment != null) {
|
||||
/*
|
||||
value = map.get(key);
|
||||
if(value == null) {
|
||||
value = ...
|
||||
}
|
||||
*/
|
||||
if (!mySuggestMapGetOrDefault) return;
|
||||
if (ExpressionUtils.isSimpleExpression(assignment.getRExpression()) &&
|
||||
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"));
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
value = map.get(key);
|
||||
if(value == null) {
|
||||
value = ...
|
||||
map.put(key, value);
|
||||
}
|
||||
*/
|
||||
PsiExpression key = getArguments[0];
|
||||
PsiExpression mapExpression = getCall.getMethodExpression().getQualifierExpression();
|
||||
PsiExpression lambdaCandidate = extractLambdaCandidate(thenBranch, mapExpression, key, value);
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static PsiExpression extractLambdaCandidate(PsiStatement statement, PsiExpression mapExpression,
|
||||
PsiExpression keyExpression, PsiReferenceExpression valueExpression) {
|
||||
EquivalenceChecker equivalence = EquivalenceChecker.getCanonicalPsiEquivalence();
|
||||
PsiAssignmentExpression assignment;
|
||||
PsiExpression putValue = extractPutValue(statement, mapExpression, keyExpression);
|
||||
if(putValue != null) {
|
||||
// like map.put(key, val = new ArrayList<>());
|
||||
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;
|
||||
putValue = extractPutValue(statements[1], mapExpression, keyExpression);
|
||||
if (!equivalence.expressionsAreEquivalent(valueExpression, putValue)) 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 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;
|
||||
PsiBinaryExpression binOp = (PsiBinaryExpression)condition;
|
||||
if(!binOp.getOperationTokenType().equals(JavaTokenType.EQEQ)) return null;
|
||||
PsiExpression value = Java8CollectionsApiInspection.getValueComparedWithNull(binOp);
|
||||
if(!(value instanceof PsiReferenceExpression)) return null;
|
||||
return (PsiReferenceExpression)value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Contract("_, null -> null")
|
||||
static PsiMethodCallExpression tryExtractMapGetCall(PsiReferenceExpression target, PsiElement element) {
|
||||
if(element instanceof PsiDeclarationStatement) {
|
||||
PsiDeclarationStatement declaration = (PsiDeclarationStatement)element;
|
||||
PsiElement[] elements = declaration.getDeclaredElements();
|
||||
if(elements.length > 0) {
|
||||
PsiElement lastDeclaration = elements[elements.length - 1];
|
||||
if(lastDeclaration instanceof PsiLocalVariable && target.isReferenceTo(lastDeclaration)) {
|
||||
PsiLocalVariable var = (PsiLocalVariable)lastDeclaration;
|
||||
PsiExpression initializer = PsiUtil.skipParenthesizedExprDown(var.getInitializer());
|
||||
if (initializer instanceof PsiMethodCallExpression &&
|
||||
Java8CollectionsApiInspection.isJavaUtilMapMethodWithName((PsiMethodCallExpression)initializer, "get")) {
|
||||
return (PsiMethodCallExpression)initializer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(element);
|
||||
if(assignment != null) {
|
||||
PsiExpression lValue = assignment.getLExpression();
|
||||
if (lValue instanceof PsiReferenceExpression &&
|
||||
EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(target, lValue)) {
|
||||
PsiExpression rValue = PsiUtil.skipParenthesizedExprDown(assignment.getRExpression());
|
||||
if (rValue instanceof PsiMethodCallExpression &&
|
||||
Java8CollectionsApiInspection.isJavaUtilMapMethodWithName((PsiMethodCallExpression)rValue, "get")) {
|
||||
return (PsiMethodCallExpression)rValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ReplaceGetNullCheck implements LocalQuickFix {
|
||||
private final String myMethodName;
|
||||
|
||||
ReplaceGetNullCheck(String methodName) {
|
||||
myMethodName = methodName;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return QuickFixBundle.message("java.8.collections.api.inspection.fix.text", myMethodName);
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return QuickFixBundle.message("java.8.replace.map.get.inspection.fix.family.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiElement element = descriptor.getStartElement();
|
||||
PsiIfStatement ifStatement = PsiTreeUtil.getParentOfType(element, PsiIfStatement.class);
|
||||
if(ifStatement == null) return;
|
||||
PsiReferenceExpression value = getReferenceComparedWithNull(ifStatement.getCondition());
|
||||
if(value == null) return;
|
||||
PsiElement statement = PsiTreeUtil.skipSiblingsBackward(ifStatement, PsiWhiteSpace.class, PsiComment.class);
|
||||
PsiMethodCallExpression getCall = tryExtractMapGetCall(value, statement);
|
||||
if(getCall == null || !Java8CollectionsApiInspection.isJavaUtilMapMethodWithName(getCall, "get")) return;
|
||||
PsiExpression[] args = getCall.getArgumentList().getExpressions();
|
||||
if(args.length != 1) return;
|
||||
PsiStatement thenBranch = ControlFlowUtils.stripBraces(ifStatement.getThenBranch());
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(thenBranch);
|
||||
CommentTracker ct = new CommentTracker();
|
||||
PsiReferenceExpression methodExpression = getCall.getMethodExpression();
|
||||
if(assignment != null) {
|
||||
PsiExpression defaultValue = assignment.getRExpression();
|
||||
if (!ExpressionUtils.isSimpleExpression(defaultValue)) return;
|
||||
methodExpression.handleElementRename(myMethodName);
|
||||
getCall.getArgumentList().add(ct.markUnchanged(defaultValue));
|
||||
} else {
|
||||
PsiExpression lambdaCandidate = extractLambdaCandidate(thenBranch, methodExpression.getQualifierExpression(), args[0], value);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
-156
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection.java18api;
|
||||
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Couple;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class ReplaceConditionalMapPutFix implements LocalQuickFix {
|
||||
private static final String COMPUTE_IF_ABSENT_METHOD = "computeIfAbsent";
|
||||
private static final String PUT_IF_ABSENT_METHOD = "putIfAbsent";
|
||||
|
||||
private final SmartPsiElementPointer<PsiMethodCallExpression> myPutExpressionPointer;
|
||||
private final String myMethodName;
|
||||
|
||||
public ReplaceConditionalMapPutFix(PsiMethodCallExpression putExpression, boolean useComputeIfAbsent) {
|
||||
final SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(putExpression.getProject());
|
||||
myPutExpressionPointer = smartPointerManager.createSmartPsiElementPointer(putExpression);
|
||||
myMethodName = useComputeIfAbsent ? COMPUTE_IF_ABSENT_METHOD : PUT_IF_ABSENT_METHOD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement conditionalOperator = descriptor.getPsiElement();
|
||||
if (conditionalOperator == null) return;
|
||||
final ConditionalOperatorHelper operatorHelper = getHelper(conditionalOperator);
|
||||
|
||||
final PsiMethodCallExpression putExpression = myPutExpressionPointer.getElement();
|
||||
if (putExpression == null) return;
|
||||
|
||||
PsiElement putContainingBranch = null;
|
||||
for (PsiElement branch : operatorHelper.getBranches(conditionalOperator)) {
|
||||
if (branch != null && PsiTreeUtil.isAncestor(branch, putExpression, false)) {
|
||||
putContainingBranch = branch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (putContainingBranch == null) return;
|
||||
|
||||
final PsiExpression[] arguments = putExpression.getArgumentList().getExpressions();
|
||||
final PsiElement qualifier = putExpression.getMethodExpression().getQualifier();
|
||||
if (qualifier == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
|
||||
|
||||
final PsiElement putContainingElement = operatorHelper.getPutContainingElement(putExpression);
|
||||
final Couple<String> boundText = getBoundText(putContainingElement, putExpression);
|
||||
|
||||
String valueArgument = putExpression.getArgumentList().getExpressions()[1].getText();
|
||||
if(myMethodName.equals(COMPUTE_IF_ABSENT_METHOD)) {
|
||||
String varName = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName("k", putExpression, true);
|
||||
valueArgument = varName + " -> " + valueArgument;
|
||||
}
|
||||
final PsiStatement newStatement =
|
||||
elementFactory.createStatementFromText(boundText.getFirst() + qualifier.getText() + "." + myMethodName +
|
||||
"(" + arguments[0].getText() + "," + valueArgument + ")" + boundText.getSecond(),
|
||||
conditionalOperator);
|
||||
conditionalOperator.replace(newStatement);
|
||||
}
|
||||
|
||||
private static Couple<String> getBoundText(@NotNull PsiElement parent, @NotNull PsiElement child) {
|
||||
final TextRange childRange = child.getTextRange();
|
||||
final int parentStartOffset = parent.getTextRange().getStartOffset();
|
||||
final String parentText = parent.getText();
|
||||
return Couple.of(parentText.substring(0, childRange.getStartOffset() - parentStartOffset),
|
||||
parentText.substring(childRange.getEndOffset() - parentStartOffset));
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return QuickFixBundle.message("java.8.collections.api.inspection.fix.family.name");
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return QuickFixBundle.message("java.8.collections.api.inspection.fix.text", myMethodName);
|
||||
}
|
||||
|
||||
private static ConditionalOperatorHelper getHelper(PsiElement element) {
|
||||
return element instanceof PsiConditionalExpression ? new ConditionalExpressionHelper() : new IfStatementHelper();
|
||||
}
|
||||
|
||||
interface ConditionalOperatorHelper {
|
||||
@NotNull
|
||||
PsiElement[] getBranches(PsiElement element);
|
||||
|
||||
@NotNull
|
||||
PsiElement getPutContainingElement(PsiElement putElement);
|
||||
}
|
||||
|
||||
private static class ConditionalExpressionHelper implements ConditionalOperatorHelper {
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElement[] getBranches(PsiElement element) {
|
||||
final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression)element;
|
||||
return new PsiElement[]{conditionalExpression.getThenExpression(), conditionalExpression.getElseExpression()};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElement getPutContainingElement(PsiElement putElement) {
|
||||
for (PsiElement element : getBranches(PsiTreeUtil.getParentOfType(putElement, PsiConditionalExpression.class))) {
|
||||
if (PsiTreeUtil.isAncestor(element, putElement, false)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
private static class IfStatementHelper implements ConditionalOperatorHelper {
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElement[] getBranches(PsiElement element) {
|
||||
final PsiIfStatement ifStatement = (PsiIfStatement)element;
|
||||
return new PsiElement[]{ifStatement.getThenBranch(), ifStatement.getElseBranch()};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElement getPutContainingElement(PsiElement putElement) {
|
||||
return PsiTreeUtil.getParentOfType(putElement, PsiStatement.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection.java18api;
|
||||
|
||||
import com.intellij.codeInsight.PsiEquivalenceUtil;
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
|
||||
import com.intellij.codeInspection.util.LambdaGenerationUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.ig.psiutils.*;
|
||||
import one.util.streamex.IntStreamEx;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
public class Java8MapApiInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
private static final Logger LOG = Logger.getInstance(Java8MapApiInspection.class);
|
||||
public static final String SHORT_NAME = "Java8MapApi";
|
||||
|
||||
public boolean mySuggestMapGetOrDefault = true;
|
||||
public boolean mySuggestMapComputeIfAbsent = true;
|
||||
public boolean mySuggestMapPutIfAbsent = true;
|
||||
public boolean myTreatGetNullAsContainsKey = false;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
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");
|
||||
panel.addCheckbox("Treat 'get(k) != null' the same as 'containsKey(k)' (may change semantics)", "myTreatGetNullAsContainsKey");
|
||||
return panel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
|
||||
if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) {
|
||||
return PsiElementVisitor.EMPTY_VISITOR;
|
||||
}
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitConditionalExpression(PsiConditionalExpression expression) {
|
||||
MapCheckCondition condition = fromTernary(expression);
|
||||
if(condition == null || condition.hasVariable()) return;
|
||||
PsiExpression existsBranch = condition.getExistsBranch(expression.getThenExpression(), expression.getElseExpression());
|
||||
PsiExpression noneBranch = condition.getNoneBranch(expression.getThenExpression(), expression.getElseExpression());
|
||||
processGetPut(condition, expression, existsBranch, existsBranch, noneBranch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIfStatement(PsiIfStatement statement) {
|
||||
MapCheckCondition condition = fromIfStatement(statement);
|
||||
if(condition == null) return;
|
||||
PsiStatement existsBranch = ControlFlowUtils.stripBraces(condition.getExistsBranch(statement.getThenBranch(), statement.getElseBranch()));
|
||||
PsiStatement noneBranch = ControlFlowUtils.stripBraces(condition.getNoneBranch(statement.getThenBranch(), statement.getElseBranch()));
|
||||
if(existsBranch == null) {
|
||||
processSingleBranch(statement, condition, noneBranch);
|
||||
} else {
|
||||
if(condition.hasVariable()) return;
|
||||
EquivalenceChecker.Decision decision =
|
||||
EquivalenceChecker.getCanonicalPsiEquivalence().statementsAreEquivalentDecision(noneBranch, existsBranch);
|
||||
|
||||
processGetPut(condition, statement, existsBranch, decision.getRightDiff(), decision.getLeftDiff());
|
||||
}
|
||||
}
|
||||
|
||||
private void processGetPut(MapCheckCondition condition, PsiElement toRemove, PsiElement result, PsiElement exists, PsiElement none) {
|
||||
if(!(exists instanceof PsiExpression)) return;
|
||||
PsiMethodCallExpression getCall = extractMapMethodCall((PsiExpression)exists, "get");
|
||||
if (getCall == null || !condition.isMap(getCall.getMethodExpression().getQualifierExpression())) return;
|
||||
PsiExpression[] getArgs = getCall.getArgumentList().getExpressions();
|
||||
if (getArgs.length != 1 || !condition.isKey(getArgs[0])) return;
|
||||
|
||||
if(!(none instanceof PsiExpression)) return;
|
||||
PsiExpression noneExpression = (PsiExpression)none;
|
||||
PsiMethodCallExpression putCall = extractMapMethodCall(noneExpression, "put");
|
||||
if (mySuggestMapPutIfAbsent &&
|
||||
putCall != null &&
|
||||
condition.isGetNull() &&
|
||||
condition.isMap(putCall.getMethodExpression().getQualifierExpression())) {
|
||||
PsiExpression[] putArgs = putCall.getArgumentList().getExpressions();
|
||||
if (putArgs.length != 2 || !condition.isKey(putArgs[0]) || !ExpressionUtils.isSimpleExpression(putArgs[1])) return;
|
||||
condition.register(holder, new ReplaceWithSingleMapOperation("putIfAbsent", getCall, putArgs[1], toRemove, result));
|
||||
}
|
||||
if (mySuggestMapGetOrDefault && condition.isContainsKey() && ExpressionUtils.isSimpleExpression(noneExpression) &&
|
||||
!(getCall.getType() instanceof PsiCapturedWildcardType)) {
|
||||
condition.register(holder, new ReplaceWithSingleMapOperation("getOrDefault", getCall, noneExpression, toRemove,
|
||||
result));
|
||||
}
|
||||
}
|
||||
|
||||
private void processSingleBranch(PsiIfStatement statement, MapCheckCondition condition, PsiStatement noneBranch) {
|
||||
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(noneBranch);
|
||||
if(assignment != null && mySuggestMapGetOrDefault && condition.isContainsKey()) {
|
||||
/*
|
||||
value = map.get(key);
|
||||
if(value == null) {
|
||||
value = ...
|
||||
}
|
||||
*/
|
||||
if (ExpressionUtils.isSimpleExpression(assignment.getRExpression()) &&
|
||||
condition.isValueReference(assignment.getLExpression()) &&
|
||||
!condition.isValueReference(assignment.getRExpression())) {
|
||||
condition
|
||||
.register(holder, ReplaceWithSingleMapOperation.fromIf("getOrDefault", condition, statement, assignment.getRExpression()));
|
||||
}
|
||||
} else if (condition.isGetNull()) {
|
||||
/*
|
||||
value = map.get(key);
|
||||
if(value == null) {
|
||||
value = ...
|
||||
map.put(key, value);
|
||||
}
|
||||
*/
|
||||
PsiExpression lambdaCandidate = extractLambdaCandidate(condition, noneBranch);
|
||||
if (lambdaCandidate != null && mySuggestMapComputeIfAbsent) {
|
||||
condition.register(holder, ReplaceWithSingleMapOperation.fromIf("computeIfAbsent", condition, statement, lambdaCandidate));
|
||||
}
|
||||
if (lambdaCandidate == null) {
|
||||
PsiExpression expression = extractPutValue(condition, noneBranch);
|
||||
if(expression != null) {
|
||||
String replacement = null;
|
||||
if (mySuggestMapPutIfAbsent && ExpressionUtils.isSimpleExpression(expression) && !condition.isValueReference(expression)) {
|
||||
replacement = "putIfAbsent";
|
||||
}
|
||||
else if (mySuggestMapComputeIfAbsent && !condition.hasVariable()) {
|
||||
replacement = "computeIfAbsent";
|
||||
}
|
||||
if(replacement != null) {
|
||||
if(condition.hasVariable()) {
|
||||
condition.register(holder, ReplaceWithSingleMapOperation.fromIf(replacement, condition, statement, expression));
|
||||
} else {
|
||||
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(expression, PsiMethodCallExpression.class);
|
||||
LOG.assertTrue(call != null);
|
||||
condition.register(holder, new ReplaceWithSingleMapOperation(replacement, call, expression, statement, noneBranch));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static PsiExpression getValueComparedWithNull(PsiBinaryExpression binOp) {
|
||||
if(!binOp.getOperationTokenType().equals(JavaTokenType.EQEQ) &&
|
||||
!binOp.getOperationTokenType().equals(JavaTokenType.NE)) return null;
|
||||
PsiExpression left = binOp.getLOperand();
|
||||
PsiExpression right = binOp.getROperand();
|
||||
if(ExpressionUtils.isNullLiteral(right)) return left;
|
||||
if(ExpressionUtils.isNullLiteral(left)) return right;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static PsiExpression extractLambdaCandidate(MapCheckCondition condition, PsiStatement statement) {
|
||||
PsiAssignmentExpression assignment;
|
||||
PsiExpression putValue = extractPutValue(condition, statement);
|
||||
if(putValue != null) {
|
||||
// like map.put(key, val = new ArrayList<>());
|
||||
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;
|
||||
putValue = extractPutValue(condition, statements[1]);
|
||||
if (!condition.isValueReference(putValue)) return null;
|
||||
assignment = ExpressionUtils.getAssignment(statements[0]);
|
||||
}
|
||||
if (assignment == null) return null;
|
||||
PsiExpression lambdaCandidate = assignment.getRExpression();
|
||||
if (lambdaCandidate == null || !condition.isValueReference(assignment.getLExpression())) return null;
|
||||
if (!LambdaGenerationUtil.canBeUncheckedLambda(lambdaCandidate)) return null;
|
||||
return lambdaCandidate;
|
||||
}
|
||||
|
||||
@Contract("null, _ -> null")
|
||||
static PsiMethodCallExpression extractMapMethodCall(PsiExpression expression, @NotNull String expectedName) {
|
||||
expression = PsiUtil.skipParenthesizedExprDown(expression);
|
||||
if (!(expression instanceof PsiMethodCallExpression)) return null;
|
||||
PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
|
||||
if (!expectedName.equals(methodCallExpression.getMethodExpression().getReferenceName())) return null;
|
||||
final PsiMethod method = methodCallExpression.resolveMethod();
|
||||
if (method == null) return null;
|
||||
PsiMethod[] superMethods = method.findDeepestSuperMethods();
|
||||
if (superMethods.length == 0) {
|
||||
superMethods = new PsiMethod[]{method};
|
||||
}
|
||||
return StreamEx.of(superMethods).map(PsiMember::getContainingClass).nonNull().map(PsiClass::getQualifiedName)
|
||||
.has(CommonClassNames.JAVA_UTIL_MAP) ? methodCallExpression : null;
|
||||
}
|
||||
|
||||
|
||||
@Contract("_, null -> null")
|
||||
@Nullable
|
||||
private static PsiExpression extractPutValue(MapCheckCondition condition, PsiStatement statement) {
|
||||
if(!(statement instanceof PsiExpressionStatement)) return null;
|
||||
PsiMethodCallExpression putCall = extractMapMethodCall(((PsiExpressionStatement)statement).getExpression(), "put");
|
||||
if (putCall == null) return null;
|
||||
PsiExpression[] putArguments = putCall.getArgumentList().getExpressions();
|
||||
return putArguments.length == 2 &&
|
||||
condition.isMap(putCall.getMethodExpression().getQualifierExpression()) &&
|
||||
condition.isKey(putArguments[0]) ? putArguments[1] : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Contract("_, null -> null")
|
||||
static PsiMethodCallExpression tryExtractMapGetCall(PsiReferenceExpression target, PsiElement element) {
|
||||
if(element instanceof PsiDeclarationStatement) {
|
||||
PsiDeclarationStatement declaration = (PsiDeclarationStatement)element;
|
||||
PsiElement[] elements = declaration.getDeclaredElements();
|
||||
if(elements.length > 0) {
|
||||
PsiElement lastDeclaration = elements[elements.length - 1];
|
||||
if(lastDeclaration instanceof PsiLocalVariable && target.isReferenceTo(lastDeclaration)) {
|
||||
PsiLocalVariable var = (PsiLocalVariable)lastDeclaration;
|
||||
return extractMapMethodCall(var.getInitializer(), "get");
|
||||
}
|
||||
}
|
||||
}
|
||||
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(element);
|
||||
if(assignment != null) {
|
||||
PsiExpression lValue = assignment.getLExpression();
|
||||
if (lValue instanceof PsiReferenceExpression &&
|
||||
EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(target, lValue)) {
|
||||
return extractMapMethodCall(assignment.getRExpression(), "get");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
MapCheckCondition fromIfStatement(PsiIfStatement ifStatement) {
|
||||
return tryExtract(ifStatement.getCondition(), ifStatement);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
MapCheckCondition fromTernary(PsiConditionalExpression ternary) {
|
||||
PsiElement parent = ternary.getParent().getParent();
|
||||
return tryExtract(ternary.getCondition(), parent instanceof PsiStatement ? (PsiStatement)parent : null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MapCheckCondition tryExtract(PsiExpression fullCondition, @Nullable PsiStatement statement) {
|
||||
PsiExpression condition = PsiUtil.skipParenthesizedExprDown(fullCondition);
|
||||
boolean negated = false;
|
||||
while(condition != null && BoolUtils.isNegation(condition)) {
|
||||
negated ^= true;
|
||||
condition = BoolUtils.getNegated(condition);
|
||||
}
|
||||
if(condition == null) return null;
|
||||
PsiReferenceExpression valueReference = null;
|
||||
boolean containsKey = false;
|
||||
PsiMethodCallExpression call;
|
||||
if(condition instanceof PsiBinaryExpression) {
|
||||
negated ^= ((PsiBinaryExpression)condition).getOperationTokenType().equals(JavaTokenType.EQEQ);
|
||||
PsiExpression value = getValueComparedWithNull((PsiBinaryExpression)condition);
|
||||
if(value instanceof PsiReferenceExpression && statement != null) {
|
||||
valueReference = (PsiReferenceExpression)value;
|
||||
PsiElement previous = PsiTreeUtil.skipSiblingsBackward(statement, PsiWhiteSpace.class, PsiComment.class);
|
||||
call = tryExtractMapGetCall(valueReference, previous);
|
||||
} else {
|
||||
call = extractMapMethodCall(value, "get");
|
||||
}
|
||||
} else {
|
||||
call = extractMapMethodCall(condition, "containsKey");
|
||||
containsKey = true;
|
||||
}
|
||||
if(call == null) return null;
|
||||
PsiExpression mapExpression = call.getMethodExpression().getQualifierExpression();
|
||||
if(mapExpression == null) return null;
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
if(args.length != 1) return null;
|
||||
PsiExpression keyExpression = args[0];
|
||||
return new MapCheckCondition(valueReference, mapExpression, keyExpression, fullCondition, negated, containsKey);
|
||||
}
|
||||
|
||||
private static class ReplaceWithSingleMapOperation implements LocalQuickFix {
|
||||
private final String myMethodName;
|
||||
private final SmartPsiElementPointer<PsiMethodCallExpression> myCallPointer;
|
||||
private final SmartPsiElementPointer<PsiExpression> myValuePointer;
|
||||
private final SmartPsiElementPointer<PsiElement> myRemovedPointer;
|
||||
private final SmartPsiElementPointer<PsiElement> myResultPointer;
|
||||
|
||||
ReplaceWithSingleMapOperation(String methodName, PsiMethodCallExpression call, PsiExpression value, PsiElement removed, PsiElement result) {
|
||||
myMethodName = methodName;
|
||||
SmartPointerManager manager = SmartPointerManager.getInstance(value.getProject());
|
||||
myCallPointer = manager.createSmartPsiElementPointer(call);
|
||||
myValuePointer = manager.createSmartPsiElementPointer(value);
|
||||
myRemovedPointer = manager.createSmartPsiElementPointer(removed);
|
||||
myResultPointer = manager.createSmartPsiElementPointer(result);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static ReplaceWithSingleMapOperation fromIf(String methodName, MapCheckCondition condition, PsiStatement ifStatement, PsiExpression value) {
|
||||
PsiMethodCallExpression call = condition.getCheckCall();
|
||||
PsiStatement result = PsiTreeUtil.getParentOfType(call, PsiStatement.class);
|
||||
LOG.assertTrue(result != null);
|
||||
return new ReplaceWithSingleMapOperation(methodName, call, value, ifStatement, result);
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return QuickFixBundle.message("java.8.map.api.inspection.fix.text", myMethodName);
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return QuickFixBundle.message("java.8.map.api.inspection.fix.family.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiMethodCallExpression call = myCallPointer.getElement();
|
||||
if (call == null) return;
|
||||
PsiExpressionList argsList = call.getArgumentList();
|
||||
PsiExpression[] args = argsList.getExpressions();
|
||||
if(args.length == 0) return;
|
||||
PsiExpression value = myValuePointer.getElement();
|
||||
if (value == null) return;
|
||||
PsiElement removed = myRemovedPointer.getElement();
|
||||
if (removed == null) return;
|
||||
PsiElement result = myResultPointer.getElement();
|
||||
if(result == null) return;
|
||||
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
CommentTracker ct = new CommentTracker();
|
||||
call.getMethodExpression().handleElementRename(myMethodName);
|
||||
PsiExpression replacement;
|
||||
if(myMethodName.equals("computeIfAbsent")) {
|
||||
PsiExpression key = args[0];
|
||||
List<PsiReferenceExpression> refs = Collections.emptyList();
|
||||
String nameCandidate = "k";
|
||||
if(key instanceof PsiReferenceExpression && ((PsiReferenceExpression)key).getQualifier() == null) {
|
||||
// try to use lambda parameter if key is simple reference and has the same type as map keys
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if(method != null) {
|
||||
PsiType argType = method.getParameterList().getParameters()[0].getType();
|
||||
PsiType mapKeyType = call.resolveMethodGenerics().getSubstitutor().substitute(argType);
|
||||
PsiType keyType = key.getType();
|
||||
|
||||
if(mapKeyType != null && keyType != null && keyType.isAssignableFrom(mapKeyType)) {
|
||||
PsiElement element = ((PsiReferenceExpression)key).resolve();
|
||||
refs = StreamEx.of(PsiTreeUtil.collectElementsOfType(value, PsiReferenceExpression.class))
|
||||
.filter(ref -> ref.getQualifierExpression() == null && ref.isReferenceTo(element)).toList();
|
||||
if (!refs.isEmpty()) {
|
||||
String name = ((PsiReferenceExpression)key).getReferenceName();
|
||||
// like "myVariableName" => "mvn"
|
||||
nameCandidate = IntStreamEx.ofChars(name).mapFirst(Character::toUpperCase).filter(Character::isUpperCase).charsToString()
|
||||
.toLowerCase(Locale.ENGLISH);
|
||||
if (nameCandidate.isEmpty()) {
|
||||
nameCandidate = "k";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
String varName = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(nameCandidate, value, true);
|
||||
for(PsiReferenceExpression ref : refs) {
|
||||
ref.handleElementRename(varName);
|
||||
}
|
||||
replacement = factory.createExpressionFromText(varName + " -> " + ct.text(value), value);
|
||||
} else {
|
||||
replacement = ct.markUnchanged(value);
|
||||
}
|
||||
if(args.length == 2) {
|
||||
ct.replace(args[1], replacement);
|
||||
} else {
|
||||
argsList.add(replacement);
|
||||
}
|
||||
PsiExpression expression = argsList.getExpressions()[1];
|
||||
if(expression instanceof PsiLambdaExpression) {
|
||||
LambdaCanBeMethodReferenceInspection.replaceLambdaWithMethodReference((PsiLambdaExpression)expression);
|
||||
}
|
||||
if(PsiTreeUtil.isAncestor(removed, result, true)) {
|
||||
result = ct.replaceAndRestoreComments(removed, ct.markUnchanged(result));
|
||||
} else {
|
||||
ct.deleteAndRestoreComments(removed);
|
||||
}
|
||||
CodeStyleManager.getInstance(project).reformat(result);
|
||||
}
|
||||
}
|
||||
|
||||
class MapCheckCondition {
|
||||
private final @Nullable PsiReferenceExpression myValueReference;
|
||||
private final PsiExpression myMapExpression;
|
||||
private final PsiExpression myKeyExpression;
|
||||
private final PsiExpression myFullCondition;
|
||||
private final boolean myNegated;
|
||||
private final boolean myContainsKey;
|
||||
|
||||
private MapCheckCondition(@Nullable PsiReferenceExpression valueReference,
|
||||
PsiExpression mapExpression,
|
||||
PsiExpression keyExpression,
|
||||
PsiExpression fullCondition,
|
||||
boolean negated,
|
||||
boolean containsKey) {
|
||||
myValueReference = valueReference;
|
||||
myMapExpression = mapExpression;
|
||||
myKeyExpression = keyExpression;
|
||||
myFullCondition = fullCondition;
|
||||
myNegated = negated;
|
||||
myContainsKey = containsKey;
|
||||
}
|
||||
|
||||
boolean isContainsKey() {
|
||||
return myContainsKey || myTreatGetNullAsContainsKey;
|
||||
}
|
||||
|
||||
boolean isGetNull() {
|
||||
return !myContainsKey || myTreatGetNullAsContainsKey;
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
boolean isMap(PsiExpression expression) {
|
||||
return expression != null && PsiEquivalenceUtil.areElementsEquivalent(myMapExpression, expression);
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
boolean isKey(PsiExpression expression) {
|
||||
return expression != null && PsiEquivalenceUtil.areElementsEquivalent(myKeyExpression, expression);
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
boolean isValueReference(PsiExpression expression) {
|
||||
return expression != null && myValueReference != null && PsiEquivalenceUtil.areElementsEquivalent(expression, myValueReference);
|
||||
}
|
||||
|
||||
<T extends PsiElement> T getExistsBranch(T thenBranch, T elseBranch) {
|
||||
return myNegated ? elseBranch : thenBranch;
|
||||
}
|
||||
|
||||
<T extends PsiElement> T getNoneBranch(T thenBranch, T elseBranch) {
|
||||
return myNegated ? thenBranch : elseBranch;
|
||||
}
|
||||
|
||||
boolean hasVariable() {
|
||||
return myValueReference != null;
|
||||
}
|
||||
|
||||
PsiMethodCallExpression getCheckCall() {
|
||||
return PsiTreeUtil.getParentOfType(myMapExpression, PsiMethodCallExpression.class);
|
||||
}
|
||||
|
||||
public PsiExpression getFullCondition() {
|
||||
return myFullCondition;
|
||||
}
|
||||
|
||||
public void register(ProblemsHolder holder, LocalQuickFix fix) {
|
||||
//noinspection DialogTitleCapitalization
|
||||
holder.registerProblem(getFullCondition(), QuickFixBundle.message("java.8.map.api.inspection.description"), fix);
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection.java18api;
|
||||
|
||||
import com.intellij.codeInspection.ex.InspectionElementsMerger;
|
||||
|
||||
public class Java8MapApiInspectionMerger extends InspectionElementsMerger {
|
||||
private static final String COLLECTION_API_INSPECTION = "Java8CollectionsApi";
|
||||
private static final String REPLACE_MAP_GET_INSPECTION = "Java8ReplaceMapGet";
|
||||
|
||||
@Override
|
||||
public String getMergedToolName() {
|
||||
return Java8MapApiInspection.SHORT_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSourceToolNames() {
|
||||
return new String[] {COLLECTION_API_INSPECTION, REPLACE_MAP_GET_INSPECTION};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// "Replace with 'computeIfAbsent' method call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
static class MyItem {
|
||||
String k;
|
||||
int i;
|
||||
|
||||
MyItem(String k, int i) {
|
||||
this.k = k;
|
||||
this.i = i;
|
||||
}
|
||||
}
|
||||
|
||||
public MyItem testMap(Map<String, MyItem> map, String token) {
|
||||
MyItem item = map.computeIfAbsent(token, t -> new MyItem(t, 1));
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Replace with 'computeIfAbsent' method call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
static class MyItem {
|
||||
String k;
|
||||
|
||||
MyItem(String k) {
|
||||
this.k = k;
|
||||
}
|
||||
}
|
||||
|
||||
public MyItem testMap(Map<String, MyItem> map, String token) {
|
||||
MyItem item = map.computeIfAbsent(token, MyItem::new);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// "Replace with 'computeIfAbsent' method call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
static class MyItem {
|
||||
String k;
|
||||
|
||||
MyItem(String k) {
|
||||
this.k = k;
|
||||
}
|
||||
}
|
||||
|
||||
public MyItem testMap(Map<CharSequence, MyItem> map, String token) {
|
||||
// Cannot create method reference here as MyItem wants a String, but lambda will receive a CharSequence
|
||||
MyItem item = map.computeIfAbsent(token, k -> new MyItem(token));
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Replace with 'getOrDefault' method call" "true"
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
private static final String NONE = "none";
|
||||
|
||||
private String str;
|
||||
|
||||
public void testGetOrDefault(Map<String, String> map, String key, Main other) {
|
||||
/* output none */
|
||||
System.out.println(/* output map value */ map.getOrDefault("k", NONE));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Replace with 'getOrDefault' method call" "true"
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
private static final String NONE = "none";
|
||||
|
||||
private String str;
|
||||
|
||||
public String testGetOrDefault(Map<String, String> map, String key, Main other) {
|
||||
return map.getOrDefault(key, "oops");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Replace with 'putIfAbsent' method call" "true"
|
||||
import java.util.Map;
|
||||
class Test {
|
||||
void m1(Map<String, Integer> map) {
|
||||
map.putIfAbsent("ads", i);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// "Replace with 'computeIfAbsent' method call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
static class MyItem {
|
||||
String k;
|
||||
int i;
|
||||
|
||||
MyItem(String k, int i) {
|
||||
this.k = k;
|
||||
this.i = i;
|
||||
}
|
||||
}
|
||||
|
||||
public MyItem testMap(Map<String, MyItem> map, String token) {
|
||||
MyItem item = map.get(token);
|
||||
if(item == nul<caret>l) {
|
||||
map.put(token, item = new MyItem(token, 1));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// "Replace with 'computeIfAbsent' method call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
static class MyItem {
|
||||
String k;
|
||||
|
||||
MyItem(String k) {
|
||||
this.k = k;
|
||||
}
|
||||
}
|
||||
|
||||
public MyItem testMap(Map<String, MyItem> map, String token) {
|
||||
MyItem item = map.get(token);
|
||||
if(item == nul<caret>l) {
|
||||
map.put(token, item = new MyItem(token));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// "Replace with 'computeIfAbsent' method call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
static class MyItem {
|
||||
String k;
|
||||
|
||||
MyItem(String k) {
|
||||
this.k = k;
|
||||
}
|
||||
}
|
||||
|
||||
public MyItem testMap(Map<CharSequence, MyItem> map, String token) {
|
||||
// Cannot create method reference here as MyItem wants a String, but lambda will receive a CharSequence
|
||||
MyItem item = map.get(token);
|
||||
if(item == nul<caret>l) {
|
||||
map.put(token, item = new MyItem(token));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -2,6 +2,6 @@
|
||||
import java.util.Map;
|
||||
class Test {
|
||||
void m1111(Map<String, Integer> map) {
|
||||
int i = !map.containsKey("asd") ? map.<caret>put("asd", 123) : map.get("asd");
|
||||
int i = !map.cont<caret>ainsKey("asd") ? map.put("asd", 123) : map.get("asd");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Replace with 'getOrDefault' method call" "false"
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
public CharSequence test(Map<String, ? extends CharSequence> map) {
|
||||
// cannot replace as map.getOrDefault("xyz", "none") will result in compilation error
|
||||
if(map.co<caret>ntainsKey("xyz")) {
|
||||
return map.get("xyz");
|
||||
} else {
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -8,7 +8,7 @@ public class Main {
|
||||
|
||||
public void testGetOrDefault(Map<String, String> map, String key, Main other) {
|
||||
str = map.get(key);
|
||||
if(str == nul<caret>l) {
|
||||
if(!(str != nul<caret>l)) {
|
||||
/*
|
||||
block comment
|
||||
*/
|
||||
@@ -0,0 +1,16 @@
|
||||
// "Replace with 'getOrDefault' method call" "true"
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
private static final String NONE = "none";
|
||||
|
||||
private String str;
|
||||
|
||||
public void testGetOrDefault(Map<String, String> map, String key, Main other) {
|
||||
if(map.conta<caret>insKey("k")) {
|
||||
System.out.println(/* output map value */ map.get("k"));
|
||||
} else {
|
||||
System.out.println(/* output none */ NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Replace with 'getOrDefault' method call" "true"
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
private static final String NONE = "none";
|
||||
|
||||
private String str;
|
||||
|
||||
public String testGetOrDefault(Map<String, String> map, String key, Main other) {
|
||||
return map.conta<caret>insKey(key) ? map.get(key) : "oops";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Replace with 'putIfAbsent' method call" "true"
|
||||
import java.util.Map;
|
||||
class Test {
|
||||
void m1(Map<String, Integer> map) {
|
||||
if (!map.contai<caret>nsKey("ads")) {
|
||||
map.put("ads", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.java18api.Java8CollectionsApiInspection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class Java8CollectionsApiInspectionTest extends LightQuickFixParameterizedTestCase {
|
||||
private Java8CollectionsApiInspection myInspection;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{myInspection};
|
||||
}
|
||||
|
||||
public void setUp() throws Exception {
|
||||
myInspection = new Java8CollectionsApiInspection();
|
||||
myInspection.myReportContainsCondition = true;
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public void test() throws Exception {
|
||||
doAllTests();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/inspection/java8CollectionsApi";
|
||||
}
|
||||
}
|
||||
+6
-4
@@ -16,17 +16,19 @@
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.java18api.Java8ReplaceMapGetInspection;
|
||||
import com.intellij.codeInspection.java18api.Java8MapApiInspection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
public class Java8ReplaceMapGetInspectionTest extends LightQuickFixParameterizedTestCase {
|
||||
public class Java8MapApiInspectionTest extends LightQuickFixParameterizedTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new Java8ReplaceMapGetInspection()};
|
||||
Java8MapApiInspection inspection = new Java8MapApiInspection();
|
||||
inspection.myTreatGetNullAsContainsKey = true;
|
||||
return new LocalInspectionTool[]{inspection};
|
||||
}
|
||||
|
||||
public void test() throws Exception {
|
||||
@@ -35,6 +37,6 @@ public class Java8ReplaceMapGetInspectionTest extends LightQuickFixParameterized
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/inspection/java8MapGet";
|
||||
return "/inspection/java8MapApi";
|
||||
}
|
||||
}
|
||||
@@ -283,11 +283,9 @@ wrap.long.with.math.to.int.parameter.multiple.text=Wrap {0, choice, 1#1st|2#2nd|
|
||||
|
||||
add.exception.from.field.initializer.to.constructor.throws.text=Add exception to class {0, choice, 0#default constructor|1#constructor|2#constructors} signature
|
||||
add.exception.from.field.initializer.to.constructor.throws.family.text=Add exception to class constructors signature
|
||||
java.8.collections.api.inspection.description=If statement could be replaced with single method
|
||||
java.8.collections.api.inspection.fix.family.name=Replace with single method call
|
||||
java.8.collections.api.inspection.fix.text=Replace with ''{0}'' method call
|
||||
java.8.replace.map.get.inspection.description=Map.get and condition could be replaced with single method call
|
||||
java.8.replace.map.get.inspection.fix.family.name=Replace Map.get and condition with single method call
|
||||
java.8.map.api.inspection.fix.text=Replace with ''{0}'' method call
|
||||
java.8.map.api.inspection.description=Can be replaced with single Map method call
|
||||
java.8.map.api.inspection.fix.family.name=Replace with single Map method call
|
||||
java.8.collection.removeif.inspection.description=The loop could be replaced with Collection.removeIf
|
||||
java.8.collection.removeif.inspection.fix.name=Replace the loop with Collection.removeIf
|
||||
java.8.list.sort.inspection.description=Collections.sort could be replaced with List.sort
|
||||
|
||||
@@ -589,6 +589,7 @@
|
||||
|
||||
<externalProjectDataService implementation="com.intellij.externalSystem.JavaProjectDataService"/>
|
||||
<inspectionElementsMerger implementation="com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionMerger"/>
|
||||
<inspectionElementsMerger implementation="com.intellij.codeInspection.java18api.Java8MapApiInspectionMerger"/>
|
||||
|
||||
<globalInspection groupPath="Java" language="JAVA" shortName="unused" displayName="Unused declaration" groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.declaration.redundancy" enabledByDefault="true" level="WARNING"
|
||||
@@ -824,11 +825,6 @@
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="AnonymousHasLambdaAlternative" displayName="Anonymous type has shorter lambda alternative"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.AnonymousHasLambdaAlternativeInspection" />
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="Java8CollectionsApi"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.java18api.Java8CollectionsApiInspection"
|
||||
displayName="Map.putIfAbsent() can be used"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="Java8ListSort"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="true" level="WARNING"
|
||||
@@ -839,11 +835,11 @@
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.java18api.Java8CollectionRemoveIfInspection"
|
||||
displayName="Loop can be replaced with Collection.removeIf()"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="Java8ReplaceMapGet"
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="Java8MapApi"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.java18api.Java8ReplaceMapGetInspection"
|
||||
displayName="Simplifiable conditional usage of Map.get()"/>
|
||||
implementationClass="com.intellij.codeInspection.java18api.Java8MapApiInspection"
|
||||
displayName="Replace with single Map method"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="SimplifyStreamApiCallChains"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.declaration.redundancy" enabledByDefault="true" level="WARNING"
|
||||
|
||||
Reference in New Issue
Block a user