Java8CollectionsApiInspection is split into 4 separate inspections

This commit is contained in:
Tagir Valeev
2016-10-04 13:15:57 +07:00
parent c6cf323132
commit 6629cc0fb1
37 changed files with 745 additions and 451 deletions
@@ -0,0 +1,197 @@
/*
* 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.FileModificationService;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.util.LambdaGenerationUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
/**
* @author Tagir Valeev
*/
public class Java8CollectionRemoveIfInspection extends BaseJavaBatchLocalInspectionTool {
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) {
return PsiElementVisitor.EMPTY_VISITOR;
}
return new JavaElementVisitor() {
void handleIteratorLoop(PsiLoopStatement statement, PsiJavaToken endToken, IteratorDeclaration declaration) {
if (endToken == null) return;
PsiStatement body = statement.getBody();
if(!(body instanceof PsiBlockStatement)) return;
PsiStatement[] statements = ((PsiBlockStatement)body).getCodeBlock().getStatements();
if(statements.length != 2 || !(statements[1] instanceof PsiIfStatement)) return;
PsiVariable element = declaration.getNextElementVariable(statements[0]);
if(element == null) return;
PsiIfStatement ifStatement = (PsiIfStatement)statements[1];
PsiExpression condition = ifStatement.getCondition();
if(condition == null || ifStatement.getElseBranch() != null) return;
PsiStatement thenStatement = ControlFlowUtils.stripBraces(ifStatement.getThenBranch());
if(!(thenStatement instanceof PsiExpressionStatement)) return;
if(!declaration.isIteratorMethodCall(((PsiExpressionStatement)thenStatement).getExpression(), "remove")) return;
if(!LambdaGenerationUtil.canBeUncheckedLambda(condition)) return;
//noinspection DialogTitleCapitalization
holder.registerProblem(statement, new TextRange(0, endToken.getTextOffset() - statement.getTextOffset() + 1),
QuickFixBundle.message("java.8.collection.removeif.inspection.description"),
new ReplaceWithRemoveIfQuickFix());
}
@Override
public void visitForStatement(PsiForStatement statement) {
super.visitForStatement(statement);
PsiStatement initialization = statement.getInitialization();
IteratorDeclaration declaration = IteratorDeclaration.extract(initialization);
if(declaration == null) return;
if(statement.getUpdate() != null) return;
if(!declaration.isHasNextCall(statement.getCondition())) return;
handleIteratorLoop(statement, statement.getRParenth(), declaration);
}
@Override
public void visitWhileStatement(PsiWhileStatement statement) {
super.visitWhileStatement(statement);
PsiElement previous = PsiTreeUtil.skipSiblingsBackward(statement, PsiComment.class, PsiWhiteSpace.class);
if(!(previous instanceof PsiDeclarationStatement)) return;
IteratorDeclaration declaration = IteratorDeclaration.extract((PsiStatement)previous);
if(declaration == null || !declaration.isHasNextCall(statement.getCondition())) return;
if(!ReferencesSearch.search(declaration.myIterator, declaration.myIterator.getUseScope()).forEach(ref -> {
return PsiTreeUtil.isAncestor(statement, ref.getElement(), true);
})) return;
handleIteratorLoop(statement, statement.getRParenth(), declaration);
}
};
}
private static class ReplaceWithRemoveIfQuickFix implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getFamilyName() {
return QuickFixBundle.message("java.8.collection.removeif.inspection.fix.name");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getStartElement();
if(!(element instanceof PsiLoopStatement)) return;
PsiLoopStatement loop = (PsiLoopStatement)element;
IteratorDeclaration declaration;
PsiElement previous = null;
if(loop instanceof PsiForStatement) {
declaration = IteratorDeclaration.extract(((PsiForStatement)loop).getInitialization());
} else if(loop instanceof PsiWhileStatement) {
previous = PsiTreeUtil.skipSiblingsBackward(loop, PsiComment.class, PsiWhiteSpace.class);
if(!(previous instanceof PsiDeclarationStatement)) return;
declaration = IteratorDeclaration.extract((PsiStatement)previous);
} else return;
if(declaration == null) return;
PsiStatement body = loop.getBody();
if(!(body instanceof PsiBlockStatement)) return;
PsiStatement[] statements = ((PsiBlockStatement)body).getCodeBlock().getStatements();
if(statements.length != 2 || !(statements[1] instanceof PsiIfStatement)) return;
PsiVariable variable = declaration.getNextElementVariable(statements[0]);
if(variable == null) return;
PsiExpression condition = ((PsiIfStatement)statements[1]).getCondition();
if(condition == null) return;
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
String replacement = (declaration.myCollection == null ? "" : declaration.myCollection.getText() + ".") +
"removeIf(" + LambdaUtil.createLambda(variable, condition) + ");";
Collection<PsiComment> comments = ContainerUtil.map(PsiTreeUtil.findChildrenOfType(loop, PsiComment.class),
comment -> (PsiComment)comment.copy());
PsiElement result = loop.replace(JavaPsiFacade.getElementFactory(project).createStatementFromText(replacement, loop));
if(previous != null) previous.delete();
LambdaCanBeMethodReferenceInspection.replaceAllLambdasWithMethodReferences(result);
CodeStyleManager.getInstance(project).reformat(result);
comments.forEach(comment -> result.getParent().addBefore(comment, result));
}
}
private static class IteratorDeclaration {
private final @NotNull PsiLocalVariable myIterator;
private final @Nullable PsiExpression myCollection;
private IteratorDeclaration(@NotNull PsiLocalVariable iterator, @Nullable PsiExpression collection) {
myIterator = iterator;
myCollection = collection;
}
public boolean isHasNextCall(PsiExpression condition) {
return isIteratorMethodCall(condition, "hasNext");
}
boolean isIteratorMethodCall(PsiExpression candidate, String method) {
if(!(candidate instanceof PsiMethodCallExpression)) return false;
PsiMethodCallExpression call = (PsiMethodCallExpression)candidate;
if(call.getArgumentList().getExpressions().length != 0) return false;
PsiReferenceExpression expression = call.getMethodExpression();
if(!method.equals(expression.getReferenceName())) return false;
PsiExpression qualifier = expression.getQualifierExpression();
if(!(qualifier instanceof PsiReferenceExpression)) return false;
return ((PsiReferenceExpression)qualifier).resolve() == myIterator;
}
public PsiVariable getNextElementVariable(PsiStatement statement) {
if(!(statement instanceof PsiDeclarationStatement)) return null;
PsiDeclarationStatement declaration = (PsiDeclarationStatement)statement;
if(declaration.getDeclaredElements().length != 1) return null;
PsiElement element = declaration.getDeclaredElements()[0];
if(!(element instanceof PsiLocalVariable)) return null;
PsiLocalVariable var = (PsiLocalVariable)element;
if(!isIteratorMethodCall(var.getInitializer(), "next")) return null;
return var;
}
@Contract("null -> null")
static IteratorDeclaration extract(PsiStatement statement) {
if(!(statement instanceof PsiDeclarationStatement)) return null;
PsiDeclarationStatement declaration = (PsiDeclarationStatement)statement;
if(declaration.getDeclaredElements().length != 1) return null;
PsiElement element = declaration.getDeclaredElements()[0];
if(!(element instanceof PsiLocalVariable)) return null;
PsiLocalVariable variable = (PsiLocalVariable)element;
PsiExpression initializer = variable.getInitializer();
if(!(initializer instanceof PsiMethodCallExpression)) return null;
PsiMethodCallExpression call = (PsiMethodCallExpression)initializer;
if(call.getArgumentList().getExpressions().length != 0) return null;
PsiReferenceExpression methodExpression = call.getMethodExpression();
if(!"iterator".equals(methodExpression.getReferenceName())) return null;
PsiMethod method = call.resolveMethod();
if(method == null || !InheritanceUtil.isInheritor(method.getContainingClass(), CommonClassNames.JAVA_UTIL_COLLECTION)) return null;
PsiType type = variable.getType();
if(!(type instanceof PsiClassType) || !((PsiClassType)type).rawType().equalsToText(CommonClassNames.JAVA_UTIL_ITERATOR)) return null;
return new IteratorDeclaration(variable, methodExpression.getQualifierExpression());
}
}
}
@@ -15,36 +15,20 @@
*/
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.*;
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
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.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import com.siyeh.ig.psiutils.EquivalenceChecker;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.ParenthesesUtils;
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.Collection;
/**
* @author Dmitry Batkovich
@@ -53,21 +37,11 @@ public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionT
private static final Logger LOG = Logger.getInstance(Java8CollectionsApiInspection.class);
public boolean myReportContainsCondition;
public boolean mySuggestCollectionRemoveIf = true;
public boolean mySuggestListSort = true;
public boolean mySuggestMapGetOrDefault = true;
public boolean mySuggestMapPutIfAbsent = true;
public boolean mySuggestMapComputeIfAbsent = true;
@Nullable
@Override
public JComponent createOptionsPanel() {
MultipleCheckboxOptionsPanel panel = new MultipleCheckboxOptionsPanel(this);
panel.addCheckbox("Suggest conversion to Collection.removeIf", "mySuggestCollectionRemoveIf");
panel.addCheckbox("Suggest conversion to List.sort", "mySuggestListSort");
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("Report when \'containsKey\' is used in condition (may change semantics)", "myReportContainsCondition");
return panel;
}
@@ -79,70 +53,6 @@ public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionT
return PsiElementVisitor.EMPTY_VISITOR;
}
return new JavaElementVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
if (!mySuggestListSort) return;
PsiElement nameElement = expression.getMethodExpression().getReferenceNameElement();
if(nameElement != null && expression.getArgumentList().getExpressions().length == 2 &&
"sort".equals(nameElement.getText())) {
PsiMethod method = expression.resolveMethod();
if(method != null) {
PsiClass containingClass = method.getContainingClass();
if(containingClass != null && CommonClassNames.JAVA_UTIL_COLLECTIONS.equals(containingClass.getQualifiedName())) {
//noinspection DialogTitleCapitalization
holder.registerProblem(nameElement, QuickFixBundle.message("java.8.collections.api.inspection.sort.description"),
new ReplaceWithListSortFix());
}
}
}
}
void handleIteratorLoop(PsiLoopStatement statement, PsiJavaToken endToken, IteratorDeclaration declaration) {
if (!mySuggestCollectionRemoveIf || endToken == null) return;
PsiStatement body = statement.getBody();
if(!(body instanceof PsiBlockStatement)) return;
PsiStatement[] statements = ((PsiBlockStatement)body).getCodeBlock().getStatements();
if(statements.length != 2 || !(statements[1] instanceof PsiIfStatement)) return;
PsiVariable element = declaration.getNextElementVariable(statements[0]);
if(element == null) return;
PsiIfStatement ifStatement = (PsiIfStatement)statements[1];
PsiExpression condition = ifStatement.getCondition();
if(condition == null || ifStatement.getElseBranch() != null) return;
PsiStatement thenStatement = ControlFlowUtils.stripBraces(ifStatement.getThenBranch());
if(!(thenStatement instanceof PsiExpressionStatement)) return;
if(!declaration.isIteratorMethodCall(((PsiExpressionStatement)thenStatement).getExpression(), "remove")) return;
if(!LambdaGenerationUtil.canBeUncheckedLambda(condition)) return;
//noinspection DialogTitleCapitalization
holder.registerProblem(statement, new TextRange(0, endToken.getTextOffset() - statement.getTextOffset() + 1),
QuickFixBundle.message("java.8.collections.api.inspection.remove.description"),
new ReplaceWithRemoveIfQuickFix());
}
@Override
public void visitForStatement(PsiForStatement statement) {
super.visitForStatement(statement);
PsiStatement initialization = statement.getInitialization();
IteratorDeclaration declaration = IteratorDeclaration.extract(initialization);
if(declaration == null) return;
if(statement.getUpdate() != null) return;
if(!declaration.isHasNextCall(statement.getCondition())) return;
handleIteratorLoop(statement, statement.getRParenth(), declaration);
}
@Override
public void visitWhileStatement(PsiWhileStatement statement) {
super.visitWhileStatement(statement);
PsiElement previous = PsiTreeUtil.skipSiblingsBackward(statement, PsiComment.class, PsiWhiteSpace.class);
if(!(previous instanceof PsiDeclarationStatement)) return;
IteratorDeclaration declaration = IteratorDeclaration.extract((PsiStatement)previous);
if(declaration == null || !declaration.isHasNextCall(statement.getCondition())) return;
if(!ReferencesSearch.search(declaration.myIterator, declaration.myIterator.getUseScope()).forEach(ref -> {
return PsiTreeUtil.isAncestor(statement, ref.getElement(), true);
})) return;
handleIteratorLoop(statement, statement.getRParenth(), declaration);
}
@Override
public void visitConditionalExpression(PsiConditionalExpression expression) {
final ConditionInfo conditionInfo = extractConditionInfo(expression.getCondition());
@@ -158,7 +68,6 @@ public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionT
@Override
public void visitIfStatement(PsiIfStatement statement) {
handleGetWithVariable(holder, statement);
final PsiExpression condition = statement.getCondition();
final ConditionInfo conditionInfo = extractConditionInfo(condition);
if (conditionInfo == null) return;
@@ -183,123 +92,9 @@ public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionT
holder, statement);
}
}
private void handleGetWithVariable(ProblemsHolder holder, 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)) {
holder.registerProblem(condition, QuickFixBundle.message("java.8.collections.api.inspection.description"),
new ReplaceGetNullCheck("getOrDefault"));
}
} else if(thenBranch instanceof PsiBlockStatement) {
/*
value = map.get(key);
if(value == null) {
value = ...
map.put(key, value);
}
*/
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(!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;
}
holder.registerProblem(condition, QuickFixBundle.message("java.8.collections.api.inspection.description"),
new ReplaceGetNullCheck("computeIfAbsent"));
}
}
};
}
@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 = 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 && lastDeclaration == target.resolve()) {
PsiLocalVariable var = (PsiLocalVariable)lastDeclaration;
PsiExpression initializer = PsiUtil.skipParenthesizedExprDown(var.getInitializer());
if (initializer instanceof PsiMethodCallExpression &&
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 && isJavaUtilMapMethodWithName((PsiMethodCallExpression)rValue, "get")) {
return (PsiMethodCallExpression)rValue;
}
}
}
return null;
}
@Nullable
private ConditionInfo extractConditionInfo(PsiExpression condition) {
final ConditionInfo info = extractConditionInfoIfGet(condition);
@@ -310,7 +105,7 @@ public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionT
}
@Nullable
private static PsiExpression getValueComparedWithNull(PsiBinaryExpression binOp) {
static PsiExpression getValueComparedWithNull(PsiBinaryExpression binOp) {
if(!binOp.getOperationTokenType().equals(JavaTokenType.EQEQ) &&
!binOp.getOperationTokenType().equals(JavaTokenType.NE)) return null;
PsiExpression left = binOp.getLOperand();
@@ -371,7 +166,7 @@ public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionT
return new ConditionInfo(containsQualifier, containsKey, inverted);
}
private void analyzeCorrespondenceOfPutAndGet(@NotNull PsiElement adjustedElseBranch,
private static void analyzeCorrespondenceOfPutAndGet(@NotNull PsiElement adjustedElseBranch,
@Nullable PsiElement adjustedThenBranch,
@Nullable PsiExpression containsQualifier,
@Nullable PsiExpression containsKey,
@@ -379,7 +174,6 @@ public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionT
@NotNull PsiElement context) {
final PsiElement maybePutMethodCall;
final PsiElement maybeGetMethodCall;
if (!mySuggestMapPutIfAbsent) return;
if (adjustedThenBranch == null) {
maybeGetMethodCall = null;
if (adjustedElseBranch instanceof PsiExpressionStatement) {
@@ -445,7 +239,7 @@ public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionT
}
}
private static boolean isJavaUtilMapMethodWithName(@NotNull PsiMethodCallExpression methodCallExpression, @NotNull String expectedName) {
static boolean isJavaUtilMapMethodWithName(@NotNull PsiMethodCallExpression methodCallExpression, @NotNull String expectedName) {
if (!expectedName.equals(methodCallExpression.getMethodExpression().getReferenceName())) {
return false;
}
@@ -482,198 +276,4 @@ public class Java8CollectionsApiInspection extends BaseJavaBatchLocalInspectionT
return myInverted;
}
}
private static class ReplaceWithListSortFix implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getFamilyName() {
return QuickFixBundle.message("java.8.collections.api.inspection.sort.fix.name");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getStartElement();
PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
if(methodCallExpression != null) {
PsiExpression[] args = methodCallExpression.getArgumentList().getExpressions();
if(args.length == 2) {
PsiExpression list = args[0];
PsiExpression comparator = args[1];
String replacement =
ParenthesesUtils.getText(list, ParenthesesUtils.METHOD_CALL_PRECEDENCE) + ".sort(" + comparator.getText() + ")";
if (!FileModificationService.getInstance().preparePsiElementForWrite(element.getContainingFile())) return;
methodCallExpression
.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText(replacement, methodCallExpression));
}
}
}
}
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.collections.api.inspection.get.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 || !isJavaUtilMapMethodWithName(getCall, "get")) return;
PsiElement nameElement = getCall.getMethodExpression().getReferenceNameElement();
if(nameElement == null) return;
PsiExpression[] args = getCall.getArgumentList().getExpressions();
if(args.length != 1) return;
PsiStatement thenBranch = ControlFlowUtils.stripBraces(ifStatement.getThenBranch());
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();
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;
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));
}
}
private static class ReplaceWithRemoveIfQuickFix implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getFamilyName() {
return QuickFixBundle.message("java.8.collections.api.inspection.remove.fix.name");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getStartElement();
if(!(element instanceof PsiLoopStatement)) return;
PsiLoopStatement loop = (PsiLoopStatement)element;
IteratorDeclaration declaration;
PsiElement previous = null;
if(loop instanceof PsiForStatement) {
declaration = IteratorDeclaration.extract(((PsiForStatement)loop).getInitialization());
} else if(loop instanceof PsiWhileStatement) {
previous = PsiTreeUtil.skipSiblingsBackward(loop, PsiComment.class, PsiWhiteSpace.class);
if(!(previous instanceof PsiDeclarationStatement)) return;
declaration = IteratorDeclaration.extract((PsiStatement)previous);
} else return;
if(declaration == null) return;
PsiStatement body = loop.getBody();
if(!(body instanceof PsiBlockStatement)) return;
PsiStatement[] statements = ((PsiBlockStatement)body).getCodeBlock().getStatements();
if(statements.length != 2 || !(statements[1] instanceof PsiIfStatement)) return;
PsiVariable variable = declaration.getNextElementVariable(statements[0]);
if(variable == null) return;
PsiExpression condition = ((PsiIfStatement)statements[1]).getCondition();
if(condition == null) return;
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
String replacement = (declaration.myCollection == null ? "" : declaration.myCollection.getText() + ".") +
"removeIf(" + LambdaUtil.createLambda(variable, condition) + ");";
Collection<PsiComment> comments = ContainerUtil.map(PsiTreeUtil.findChildrenOfType(loop, PsiComment.class),
comment -> (PsiComment)comment.copy());
PsiElement result = loop.replace(JavaPsiFacade.getElementFactory(project).createStatementFromText(replacement, loop));
if(previous != null) previous.delete();
LambdaCanBeMethodReferenceInspection.replaceAllLambdasWithMethodReferences(result);
CodeStyleManager.getInstance(project).reformat(result);
comments.forEach(comment -> result.getParent().addBefore(comment, result));
}
}
private static class IteratorDeclaration {
private final @NotNull PsiLocalVariable myIterator;
private final @Nullable PsiExpression myCollection;
private IteratorDeclaration(@NotNull PsiLocalVariable iterator, @Nullable PsiExpression collection) {
myIterator = iterator;
myCollection = collection;
}
public boolean isHasNextCall(PsiExpression condition) {
return isIteratorMethodCall(condition, "hasNext");
}
boolean isIteratorMethodCall(PsiExpression candidate, String method) {
if(!(candidate instanceof PsiMethodCallExpression)) return false;
PsiMethodCallExpression call = (PsiMethodCallExpression)candidate;
if(call.getArgumentList().getExpressions().length != 0) return false;
PsiReferenceExpression expression = call.getMethodExpression();
if(!method.equals(expression.getReferenceName())) return false;
PsiExpression qualifier = expression.getQualifierExpression();
if(!(qualifier instanceof PsiReferenceExpression)) return false;
return ((PsiReferenceExpression)qualifier).resolve() == myIterator;
}
public PsiVariable getNextElementVariable(PsiStatement statement) {
if(!(statement instanceof PsiDeclarationStatement)) return null;
PsiDeclarationStatement declaration = (PsiDeclarationStatement)statement;
if(declaration.getDeclaredElements().length != 1) return null;
PsiElement element = declaration.getDeclaredElements()[0];
if(!(element instanceof PsiLocalVariable)) return null;
PsiLocalVariable var = (PsiLocalVariable)element;
if(!isIteratorMethodCall(var.getInitializer(), "next")) return null;
return var;
}
@Contract("null -> null")
static IteratorDeclaration extract(PsiStatement statement) {
if(!(statement instanceof PsiDeclarationStatement)) return null;
PsiDeclarationStatement declaration = (PsiDeclarationStatement)statement;
if(declaration.getDeclaredElements().length != 1) return null;
PsiElement element = declaration.getDeclaredElements()[0];
if(!(element instanceof PsiLocalVariable)) return null;
PsiLocalVariable variable = (PsiLocalVariable)element;
PsiExpression initializer = variable.getInitializer();
if(!(initializer instanceof PsiMethodCallExpression)) return null;
PsiMethodCallExpression call = (PsiMethodCallExpression)initializer;
if(call.getArgumentList().getExpressions().length != 0) return null;
PsiReferenceExpression methodExpression = call.getMethodExpression();
if(!"iterator".equals(methodExpression.getReferenceName())) return null;
PsiMethod method = call.resolveMethod();
if(method == null || !InheritanceUtil.isInheritor(method.getContainingClass(), CommonClassNames.JAVA_UTIL_COLLECTION)) return null;
PsiType type = variable.getType();
if(!(type instanceof PsiClassType) || !((PsiClassType)type).rawType().equalsToText(CommonClassNames.JAVA_UTIL_ITERATOR)) return null;
return new IteratorDeclaration(variable, methodExpression.getQualifierExpression());
}
}
}
@@ -0,0 +1,90 @@
/*
* 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.FileModificationService;
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.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
/**
* @author Tagir Valeev
*/
public class Java8ListSortInspection extends BaseJavaBatchLocalInspectionTool {
@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 visitMethodCallExpression(PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
PsiElement nameElement = expression.getMethodExpression().getReferenceNameElement();
if(nameElement != null && expression.getArgumentList().getExpressions().length == 2 &&
"sort".equals(nameElement.getText())) {
PsiMethod method = expression.resolveMethod();
if(method != null) {
PsiClass containingClass = method.getContainingClass();
if(containingClass != null && CommonClassNames.JAVA_UTIL_COLLECTIONS.equals(containingClass.getQualifiedName())) {
//noinspection DialogTitleCapitalization
holder.registerProblem(nameElement, QuickFixBundle.message("java.8.list.sort.inspection.description"),
new ReplaceWithListSortFix());
}
}
}
}
};
}
private static class ReplaceWithListSortFix implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getFamilyName() {
return QuickFixBundle.message("java.8.list.sort.inspection.fix.name");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getStartElement();
PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
if(methodCallExpression != null) {
PsiExpression[] args = methodCallExpression.getArgumentList().getExpressions();
if(args.length == 2) {
PsiExpression list = args[0];
PsiExpression comparator = args[1];
String replacement =
ParenthesesUtils.getText(list, ParenthesesUtils.METHOD_CALL_PRECEDENCE) + ".sort(" + comparator.getText() + ")";
if (!FileModificationService.getInstance().preparePsiElementForWrite(element.getContainingFile())) return;
methodCallExpression
.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText(replacement, methodCallExpression));
}
}
}
}
}
@@ -0,0 +1,252 @@
/*
* 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.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.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.intellij.util.containers.ContainerUtil;
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.*;
import java.util.Collection;
/**
* @author Tagir Valeev
*/
public class Java8ReplaceMapGetInspection extends BaseJavaBatchLocalInspectionTool {
public boolean mySuggestMapGetOrDefault = true;
public boolean mySuggestMapComputeIfAbsent = 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");
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)) {
holder.registerProblem(condition, QuickFixBundle.message("java.8.replace.map.get.inspection.description"),
new ReplaceGetNullCheck("getOrDefault"));
}
} else if(thenBranch instanceof PsiBlockStatement) {
/*
value = map.get(key);
if(value == null) {
value = ...
map.put(key, value);
}
*/
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;
}
holder.registerProblem(condition, QuickFixBundle.message("java.8.replace.map.get.inspection.description"),
new ReplaceGetNullCheck("computeIfAbsent"));
}
}
};
}
@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 && lastDeclaration == target.resolve()) {
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;
PsiElement nameElement = getCall.getMethodExpression().getReferenceNameElement();
if(nameElement == null) return;
PsiExpression[] args = getCall.getArgumentList().getExpressions();
if(args.length != 1) return;
PsiStatement thenBranch = ControlFlowUtils.stripBraces(ifStatement.getThenBranch());
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();
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;
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,40 @@
/*
* 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.Java8CollectionRemoveIfInspection;
import org.jetbrains.annotations.NotNull;
/**
* @author Tagir Valeev
*/
public class Java8CollectionRemoveIfInspectionTest extends LightQuickFixParameterizedTestCase {
@NotNull
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new Java8CollectionRemoveIfInspection()};
}
public void test() throws Exception {
doAllTests();
}
@Override
protected String getBasePath() {
return "/inspection/java8CollectionRemoveIf";
}
}
@@ -0,0 +1,40 @@
/*
* 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.Java8ListSortInspection;
import org.jetbrains.annotations.NotNull;
/**
* @author Tagir Valeev
*/
public class Java8ListSortInspectionTest extends LightQuickFixParameterizedTestCase {
@NotNull
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new Java8ListSortInspection()};
}
public void test() throws Exception {
doAllTests();
}
@Override
protected String getBasePath() {
return "/inspection/java8ListSort";
}
}
@@ -0,0 +1,40 @@
/*
* 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.Java8ReplaceMapGetInspection;
import org.jetbrains.annotations.NotNull;
/**
* @author Tagir Valeev
*/
public class Java8ReplaceMapGetInspectionTest extends LightQuickFixParameterizedTestCase {
@NotNull
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new Java8ReplaceMapGetInspection()};
}
public void test() throws Exception {
doAllTests();
}
@Override
protected String getBasePath() {
return "/inspection/java8MapGet";
}
}
@@ -0,0 +1,16 @@
<html>
<body>
The inspection reports the loops which could be collapsed into single <code>Collection.removeIf</code> call in Java 8.
<!-- tooltip end -->
<p>The loops like this are reported:</p>
<pre>
for (Iterator&lt;String&gt; it = collection.iterator(); it.hasNext(); ) {
String aValue = it.next();
if(shouldBeRemoved(aValue)) {
it.remove();
}
}
</pre>
<small>New in 2016.3</small>
</body>
</html>
@@ -1,44 +1,11 @@
<html>
<body>
Inspection detects usages of pre-Java 8 Collection API methods which can be simplified using new Java 8 API.
<!-- tooltip end -->
The following cases are covered by this inspection:
<ul>
<li><b>Map.putIfAbsent</b> method could be used to replace the code like this:
<pre>
if (!map.containsKey(aKey)) {
map.put(aKey, aValue);
}
</pre>
</li>
<li><b>Map.getOrDefault</b> method could be used to replace the code like this:
<pre>
aValue = map.get(aKey);
if (aValue == null) {
aValue = "none";
}
</pre>
</li>
<li><b>Map.computeIfAbsent</b> method could be used to replace the code like this:
<pre>
List&lt;String&gt; list = map.get(key);
if (list == null) {
list = new ArrayList&lt;&gt;();
map.put(key, list);
}
</pre>
</li>
<li><b>Collection.removeIf</b> method could be used to replace the code like this:
<pre>
for (Iterator&lt;String&gt; it = collection.iterator(); it.hasNext(); ) {
String aValue = it.next();
if(shouldBeRemoved(aValue)) {
it.remove();
}
}
</pre>
</li>
<li><b>List.sort</b> instance method could be used to replace <b>Collections.sort</b> static method</li>
</ul>
Inspection detects usages of java's <b>Map</b> when they can be replaced with methods <b>putIfAbsent</b>. For example:
<code><pre>
if (!map.containsKey(aKey)) {
map.put(aKey, aValue);
}
</pre>
</code>
</body>
</html>
@@ -0,0 +1,9 @@
<html>
<body>
Inspection detects calls to <code>Collections.sort(list, comparator)</code> which could be replaced with <code>list.sort(comparator)</code>.
<!-- tooltip end -->
<p>
<small>New in 2016.3</small>
</p>
</body>
</html>
@@ -0,0 +1,27 @@
<html>
<body>
Inspection detects calls to <code>Map.get</code> which could be replaced with <code>getOrDefault</code> or <code>computeIfAbsent</code> in
Java 8.
<!-- tooltip end -->
<ul>
<li><b>Map.getOrDefault</b> method could be used to replace the code like this:
<pre>
aValue = map.get(aKey);
if (aValue == null) {
aValue = "none";
}
</pre>
</li>
<li><b>Map.computeIfAbsent</b> method could be used to replace the code like this:
<pre>
List&lt;String&gt; list = map.get(key);
if (list == null) {
list = new ArrayList&lt;&gt;();
map.put(key, list);
}
</pre>
</li>
</ul>
<small>New in 2016.3</small>
</body>
</html>
@@ -286,11 +286,12 @@ add.exception.from.field.initializer.to.constructor.throws.family.text=Add excep
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.collections.api.inspection.sort.description=Collections.sort could be replaced with List.sort
java.8.collections.api.inspection.sort.fix.name=Replace with List.sort
java.8.collections.api.inspection.get.fix.family.name=Replace Map.get and condition with single method call
java.8.collections.api.inspection.remove.description=The loop could be replaced with Collection.removeIf
java.8.collections.api.inspection.remove.fix.name=Replace the loop with Collection.removeIf
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.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
java.8.list.sort.inspection.fix.name=Replace with List.sort
wrap.with.optional.parameter.text=Wrap {0, choice, 1#1st|2#2nd|3#3rd|4#{0,number}th} parameter using ''java.util.Optional''
wrap.with.optional.single.parameter.text=Wrap using 'java.util.Optional'
+16 -1
View File
@@ -818,7 +818,22 @@
groupBundle="messages.InspectionsBundle"
groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.java18api.Java8CollectionsApiInspection"
displayName="Collection usage can be simplified in Java 8"/>
displayName="Map.putIfAbsent() can be used in Java 8"/>
<localInspection groupPath="Java" language="JAVA" shortName="Java8ListSort"
groupBundle="messages.InspectionsBundle"
groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.java18api.Java8ListSortInspection"
displayName="Collections.sort() can be replaced with List.sort() in Java 8"/>
<localInspection groupPath="Java" language="JAVA" shortName="Java8CollectionRemoveIf"
groupBundle="messages.InspectionsBundle"
groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.java18api.Java8CollectionRemoveIfInspection"
displayName="The loop can be replaced with Collection.removeIf() in Java 8"/>
<localInspection groupPath="Java" language="JAVA" shortName="Java8ReplaceMapGet"
groupBundle="messages.InspectionsBundle"
groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.java18api.Java8ReplaceMapGetInspection"
displayName="Map.get() call followed by condition can be replaced with single method in Java 8"/>
<localInspection groupPath="Java" language="JAVA" shortName="SimplifyStreamApiCallChains"
groupBundle="messages.InspectionsBundle"
groupKey="group.names.declaration.redundancy" enabledByDefault="true" level="WARNING"