mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-165942 Inspection to replace method call in a loop with bulk operation
This commit is contained in:
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* 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.PsiEquivalenceUtil;
|
||||
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
|
||||
import com.intellij.codeInspection.util.IteratorDeclaration;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.MethodCallUtils;
|
||||
import com.siyeh.ig.psiutils.VariableAccessUtils;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
public class UseBulkOperationInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
private static BulkMethodInfo[] INFOS = {
|
||||
new BulkMethodInfo(CommonClassNames.JAVA_UTIL_COLLECTION, "add", "addAll"),
|
||||
new BulkMethodInfo(CommonClassNames.JAVA_UTIL_LIST, "remove", "removeAll"),
|
||||
new BulkMethodInfo("org.springframework.data.repository.CrudRepository", "save", "save"),
|
||||
new BulkMethodInfo("org.springframework.data.repository.CrudRepository", "delete", "delete"),
|
||||
};
|
||||
|
||||
private List<BulkMethodInfo> myInfos = StreamEx.of(INFOS).toList();
|
||||
|
||||
public boolean USE_ARRAYS_AS_LIST = true;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
return new SingleCheckboxOptionsPanel("Use Arrays.asList() to adapt arrays", this, "USE_ARRAYS_AS_LIST");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BulkMethodInfo findInfo(PsiReferenceExpression ref) {
|
||||
return StreamEx.of(myInfos).findFirst(info -> info.isMyMethod(ref)).orElse(null);
|
||||
}
|
||||
|
||||
private static PsiExpression getQualifierOrThis(@NotNull PsiReferenceExpression ref) {
|
||||
PsiExpression qualifier = ref.getQualifierExpression();
|
||||
if (qualifier != null) return qualifier;
|
||||
return JavaPsiFacade.getElementFactory(ref.getProject()).createExpressionFromText("this", ref);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiExpression findIterable(PsiMethodCallExpression expression) {
|
||||
PsiElement parent = expression.getParent();
|
||||
PsiExpression[] args = expression.getArgumentList().getExpressions();
|
||||
if (args.length != 1) return null;
|
||||
PsiExpression arg = args[0];
|
||||
if (parent instanceof PsiLambdaExpression) {
|
||||
return findIterableForLambda((PsiLambdaExpression)parent, arg);
|
||||
}
|
||||
if (parent instanceof PsiExpressionStatement) {
|
||||
PsiExpressionStatement expressionStatement = (PsiExpressionStatement)parent;
|
||||
if (expressionStatement.getParent() instanceof PsiCodeBlock) {
|
||||
PsiCodeBlock codeBlock = (PsiCodeBlock)expressionStatement.getParent();
|
||||
PsiStatement[] statements = codeBlock.getStatements();
|
||||
if (codeBlock.getParent() instanceof PsiBlockStatement) {
|
||||
PsiBlockStatement blockStatement = (PsiBlockStatement)codeBlock.getParent();
|
||||
if (statements.length == 1) {
|
||||
return findIterableForSingleStatement(blockStatement, arg);
|
||||
}
|
||||
PsiElement blockParent = blockStatement.getParent();
|
||||
if (statements.length == 2 && statements[1] == parent && blockParent instanceof PsiLoopStatement) {
|
||||
IteratorDeclaration declaration = IteratorDeclaration.fromLoop((PsiLoopStatement)blockParent);
|
||||
if (declaration != null && ExpressionUtils.isReferenceTo(arg, declaration.getNextElementVariable(statements[0]))) {
|
||||
return declaration.getIterable();
|
||||
}
|
||||
if(blockParent instanceof PsiForStatement && statements[0] instanceof PsiDeclarationStatement) {
|
||||
PsiElement[] elements = ((PsiDeclarationStatement)statements[0]).getDeclaredElements();
|
||||
if(elements.length == 1 && elements[0] instanceof PsiLocalVariable) {
|
||||
PsiLocalVariable var = (PsiLocalVariable)elements[0];
|
||||
if (ExpressionUtils.isReferenceTo(arg, var)) {
|
||||
return findIterableForIndexedLoop((PsiForStatement)blockParent, var.getInitializer());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (codeBlock.getParent() instanceof PsiLambdaExpression && statements.length == 1) {
|
||||
return findIterableForLambda((PsiLambdaExpression)codeBlock.getParent(), arg);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return findIterableForSingleStatement(expressionStatement, arg);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiExpression findIterableForLambda(PsiLambdaExpression lambda, PsiExpression arg) {
|
||||
PsiParameterList parameters = lambda.getParameterList();
|
||||
if (parameters.getParametersCount() != 1) return null;
|
||||
PsiParameter parameter = parameters.getParameters()[0];
|
||||
if (ExpressionUtils.isReferenceTo(arg, parameter)) {
|
||||
return findIterableForFunction(lambda);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiExpression findIterableForSingleStatement(PsiStatement statement, PsiExpression arg) {
|
||||
PsiElement parent = statement.getParent();
|
||||
if (parent instanceof PsiForeachStatement) {
|
||||
PsiForeachStatement foreachStatement = (PsiForeachStatement)parent;
|
||||
if (ExpressionUtils.isReferenceTo(arg, foreachStatement.getIterationParameter())) {
|
||||
return foreachStatement.getIteratedValue();
|
||||
}
|
||||
}
|
||||
if (parent instanceof PsiLoopStatement) {
|
||||
IteratorDeclaration declaration = IteratorDeclaration.fromLoop((PsiLoopStatement)parent);
|
||||
if (declaration != null && declaration.isIteratorMethodCall(arg, "next")) {
|
||||
return declaration.getIterable();
|
||||
}
|
||||
}
|
||||
if (parent instanceof PsiForStatement) {
|
||||
return findIterableForIndexedLoop((PsiForStatement)parent, arg);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiExpression findIterableForIndexedLoop(PsiForStatement loop, PsiExpression getElementExpression) {
|
||||
PsiExpression indexExpression = null;
|
||||
PsiExpression iterable = null;
|
||||
// Check that getElementExpression is either list.get(idx) or arr[idx] extracting idx and list/arr
|
||||
if (getElementExpression instanceof PsiArrayAccessExpression) {
|
||||
PsiArrayAccessExpression arrayAccess = (PsiArrayAccessExpression)getElementExpression;
|
||||
indexExpression = arrayAccess.getIndexExpression();
|
||||
iterable = arrayAccess.getArrayExpression();
|
||||
}
|
||||
else if (getElementExpression instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression call = (PsiMethodCallExpression)getElementExpression;
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
if (args.length != 1 || !MethodCallUtils.isCallToMethod(call, CommonClassNames.JAVA_UTIL_LIST, null, "get", PsiType.INT)) {
|
||||
return null;
|
||||
}
|
||||
indexExpression = args[0];
|
||||
iterable = getQualifierOrThis(call.getMethodExpression());
|
||||
}
|
||||
if (iterable == null) return null;
|
||||
|
||||
// Check that loop initialization is like `int idx = 0` and loop update is like `idx++`
|
||||
PsiStatement initialization = loop.getInitialization();
|
||||
if (!(initialization instanceof PsiDeclarationStatement)) return null;
|
||||
PsiElement[] declaredElements = ((PsiDeclarationStatement)initialization).getDeclaredElements();
|
||||
if (declaredElements.length != 1 || !(declaredElements[0] instanceof PsiLocalVariable)) return null;
|
||||
PsiLocalVariable indexVariable = (PsiLocalVariable)declaredElements[0];
|
||||
if (!ExpressionUtils.isReferenceTo(indexExpression, indexVariable) ||
|
||||
!ExpressionUtils.isZero(indexVariable.getInitializer()) ||
|
||||
!VariableAccessUtils.variableIsIncremented(indexVariable, loop.getUpdate())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check that loop condition is like `idx < arr.length` or `idx < list.size()`
|
||||
PsiExpression condition = loop.getCondition();
|
||||
if (!(condition instanceof PsiBinaryExpression)) return null;
|
||||
PsiBinaryExpression binOp = (PsiBinaryExpression)condition;
|
||||
if (!binOp.getOperationTokenType().equals(JavaTokenType.LT)) return null;
|
||||
if (!ExpressionUtils.isReferenceTo(binOp.getLOperand(), indexVariable)) return null;
|
||||
PsiExpression bound = binOp.getROperand();
|
||||
if (bound instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression boundCall = (PsiMethodCallExpression)bound;
|
||||
if (!MethodCallUtils.isCallToMethod(boundCall, CommonClassNames.JAVA_UTIL_LIST, PsiType.INT, "size")) return null;
|
||||
PsiExpression sizeQualifier = getQualifierOrThis(boundCall.getMethodExpression());
|
||||
if (PsiEquivalenceUtil.areElementsEquivalent(sizeQualifier, iterable)) return sizeQualifier;
|
||||
} else {
|
||||
PsiExpression arrayExpression = ExpressionUtils.getArrayFromLengthExpression(bound);
|
||||
if (arrayExpression != null && PsiEquivalenceUtil.areElementsEquivalent(arrayExpression, iterable)) return arrayExpression;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiExpression findIterableForFunction(PsiFunctionalExpression function) {
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(function.getParent());
|
||||
if (parent instanceof PsiTypeCastExpression) parent = PsiUtil.skipParenthesizedExprUp(parent.getParent());
|
||||
if (!(parent instanceof PsiExpressionList)) return null;
|
||||
PsiExpression[] args = ((PsiExpressionList)parent).getExpressions();
|
||||
if (args.length != 1) return null;
|
||||
parent = parent.getParent();
|
||||
if (!(parent instanceof PsiMethodCallExpression)) return null;
|
||||
PsiMethodCallExpression parentCall = (PsiMethodCallExpression)parent;
|
||||
PsiExpression parentQualifier = PsiUtil.skipParenthesizedExprDown(parentCall.getMethodExpression().getQualifierExpression());
|
||||
if (MethodCallUtils.isCallToMethod(parentCall, CommonClassNames.JAVA_LANG_ITERABLE, null,
|
||||
"forEach", new PsiType[]{null})) {
|
||||
return getQualifierOrThis(parentCall.getMethodExpression());
|
||||
}
|
||||
if (MethodCallUtils.isCallToMethod(parentCall, CommonClassNames.JAVA_UTIL_STREAM_STREAM, null,
|
||||
Pattern.compile("forEach(Ordered)?"), new PsiType[]{null}) &&
|
||||
parentQualifier instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression grandParentCall = (PsiMethodCallExpression)parentQualifier;
|
||||
if (MethodCallUtils.isCallToMethod(grandParentCall, CommonClassNames.JAVA_UTIL_COLLECTION, null,
|
||||
"stream", PsiType.EMPTY_ARRAY)) {
|
||||
return getQualifierOrThis(grandParentCall.getMethodExpression());
|
||||
}
|
||||
PsiExpression[] grandParentArgs = grandParentCall.getArgumentList().getExpressions();
|
||||
if (MethodCallUtils.isCallToStaticMethod(grandParentCall, CommonClassNames.JAVA_UTIL_ARRAYS, "stream", 1)) {
|
||||
if (grandParentArgs.length == 1) {
|
||||
return grandParentArgs[0];
|
||||
}
|
||||
}
|
||||
if (MethodCallUtils.isCallToStaticMethod(grandParentCall, CommonClassNames.JAVA_UTIL_STREAM_STREAM, "of", 1)) {
|
||||
if (grandParentArgs.length == 1) {
|
||||
PsiExpression maybeArray = grandParentArgs[0];
|
||||
PsiType type = maybeArray.getType();
|
||||
if (type instanceof PsiArrayType) {
|
||||
return maybeArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression call) {
|
||||
super.visitMethodCallExpression(call);
|
||||
PsiExpression iterable = findIterable(call);
|
||||
if (iterable == null) return;
|
||||
BulkMethodInfo info = findInfo(call.getMethodExpression());
|
||||
if (info != null) register(iterable, info, call.getMethodExpression());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodReferenceExpression(PsiMethodReferenceExpression methodReference) {
|
||||
super.visitMethodReferenceExpression(methodReference);
|
||||
BulkMethodInfo info = findInfo(methodReference);
|
||||
if (info == null) return;
|
||||
PsiExpression iterable = findIterableForFunction(methodReference);
|
||||
if (iterable != null) register(iterable, info, methodReference);
|
||||
}
|
||||
|
||||
private void register(@NotNull PsiExpression iterable,
|
||||
@NotNull BulkMethodInfo info,
|
||||
@NotNull PsiReferenceExpression methodExpression) {
|
||||
PsiExpression qualifier = getQualifierOrThis(methodExpression);
|
||||
if (qualifier instanceof PsiThisExpression) {
|
||||
PsiMethod method = PsiTreeUtil.getParentOfType(iterable, PsiMethod.class);
|
||||
// Likely we are inside of the bulk method implementation
|
||||
if (method != null && method.getName().equals(info.myBulkName)) return;
|
||||
}
|
||||
if (qualifier != null && info.isSupportedIterable(qualifier, iterable, USE_ARRAYS_AS_LIST)) {
|
||||
holder.registerProblem(methodExpression,
|
||||
InspectionsBundle.message("inspection.replace.with.bulk.message", info.getReplacementName()),
|
||||
new UseBulkOperationFix(info));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class UseBulkOperationFix implements LocalQuickFix {
|
||||
private BulkMethodInfo myInfo;
|
||||
|
||||
public UseBulkOperationFix(BulkMethodInfo info) {
|
||||
myInfo = info;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return InspectionsBundle.message("inspection.replace.with.bulk.fix.name", myInfo.getReplacementName());
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return InspectionsBundle.message("inspection.replace.with.bulk.fix.family.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiElement element = descriptor.getStartElement();
|
||||
if (!(element instanceof PsiReferenceExpression)) return;
|
||||
PsiExpression qualifier = getQualifierOrThis((PsiReferenceExpression)element);
|
||||
if (qualifier == null) return;
|
||||
PsiExpression iterable;
|
||||
if (element instanceof PsiMethodReferenceExpression) {
|
||||
iterable = findIterableForFunction((PsiFunctionalExpression)element);
|
||||
}
|
||||
else {
|
||||
PsiElement parent = element.getParent();
|
||||
if (!(parent instanceof PsiMethodCallExpression)) return;
|
||||
iterable = findIterable((PsiMethodCallExpression)parent);
|
||||
}
|
||||
if (iterable == null) return;
|
||||
PsiElement parent = RefactoringUtil.getParentStatement(iterable, false);
|
||||
if (parent == null) return;
|
||||
CommentTracker ct = new CommentTracker();
|
||||
PsiType type = iterable.getType();
|
||||
String iterableText = ct.text(iterable);
|
||||
if (type instanceof PsiArrayType) {
|
||||
iterableText = CommonClassNames.JAVA_UTIL_ARRAYS + ".asList(" + iterableText + ")";
|
||||
}
|
||||
if (parent instanceof PsiDeclarationStatement) {
|
||||
PsiLoopStatement loop = PsiTreeUtil.getParentOfType(element, PsiLoopStatement.class);
|
||||
if (loop != null && loop.getParent() == parent.getParent()) {
|
||||
ct.delete(loop);
|
||||
}
|
||||
}
|
||||
PsiElement result = ct.replaceAndRestoreComments(parent, ct.text(qualifier) + "." + myInfo.myBulkName + "(" + iterableText + ")"
|
||||
+ (parent instanceof PsiStatement ? ";" : ""));
|
||||
result = JavaCodeStyleManager.getInstance(project).shortenClassReferences(result);
|
||||
CodeStyleManager.getInstance(project).reformat(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static class BulkMethodInfo {
|
||||
private final String myClassName;
|
||||
private final String mySimpleName;
|
||||
private final String myBulkName;
|
||||
|
||||
private BulkMethodInfo(String className, String simpleName, String bulkName) {
|
||||
myClassName = className;
|
||||
mySimpleName = simpleName;
|
||||
myBulkName = bulkName;
|
||||
}
|
||||
|
||||
public boolean isMyMethod(PsiReferenceExpression ref) {
|
||||
if (!mySimpleName.equals(ref.getReferenceName())) return false;
|
||||
PsiElement element = ref.resolve();
|
||||
if (!(element instanceof PsiMethod)) return false;
|
||||
PsiMethod method = (PsiMethod)element;
|
||||
PsiParameterList parameters = method.getParameterList();
|
||||
if (parameters.getParametersCount() != 1) return false;
|
||||
PsiParameter parameter = parameters.getParameters()[0];
|
||||
PsiClass parameterClass = PsiUtil.resolveClassInClassTypeOnly(parameter.getType());
|
||||
if (parameterClass == null ||
|
||||
CommonClassNames.JAVA_LANG_ITERABLE.equals(parameterClass.getQualifiedName()) ||
|
||||
CommonClassNames.JAVA_UTIL_COLLECTION.equals(parameterClass.getQualifiedName())) {
|
||||
return false;
|
||||
}
|
||||
PsiClass methodClass = method.getContainingClass();
|
||||
return methodClass != null && InheritanceUtil.isInheritor(methodClass, myClassName);
|
||||
}
|
||||
|
||||
public boolean isSupportedIterable(PsiExpression qualifier, PsiExpression iterable, boolean useArraysAsList) {
|
||||
PsiType qualifierType = qualifier.getType();
|
||||
if (!(qualifierType instanceof PsiClassType)) return false;
|
||||
PsiType type = iterable.getType();
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(iterable.getProject());
|
||||
if (type instanceof PsiArrayType) {
|
||||
PsiType componentType = ((PsiArrayType)type).getComponentType();
|
||||
if (!useArraysAsList || componentType instanceof PsiPrimitiveType) return false;
|
||||
PsiClass listClass =
|
||||
JavaPsiFacade.getInstance(iterable.getProject()).findClass(CommonClassNames.JAVA_UTIL_LIST, iterable.getResolveScope());
|
||||
if (listClass == null) return false;
|
||||
type = factory.createType(listClass, componentType);
|
||||
}
|
||||
if (!InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ITERABLE)) return false;
|
||||
PsiExpression expression =
|
||||
factory.createExpressionFromText(qualifier.getText() + "." + myBulkName + "(" + iterable.getText() + ")", iterable);
|
||||
if (!(expression instanceof PsiMethodCallExpression)) return false;
|
||||
PsiMethodCallExpression call = (PsiMethodCallExpression)expression;
|
||||
PsiMethod bulkMethod = call.resolveMethod();
|
||||
if (bulkMethod == null) return false;
|
||||
PsiParameterList parameters = bulkMethod.getParameterList();
|
||||
if (parameters.getParametersCount() != 1) return false;
|
||||
PsiType parameterType = parameters.getParameters()[0].getType();
|
||||
parameterType = call.resolveMethodGenerics().getSubstitutor().substitute(parameterType);
|
||||
PsiClass parameterClass = PsiUtil.resolveClassInClassTypeOnly(parameterType);
|
||||
return parameterClass != null &&
|
||||
(CommonClassNames.JAVA_LANG_ITERABLE.equals(parameterClass.getQualifiedName()) ||
|
||||
CommonClassNames.JAVA_UTIL_COLLECTION.equals(parameterClass.getQualifiedName())) &&
|
||||
parameterType.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
public String getReplacementName() {
|
||||
return StringUtil.getShortName(myClassName) + "." + myBulkName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myClassName + "::" + mySimpleName + " => " + myBulkName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.
|
||||
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.MapOp;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.SuggestedNameInfo;
|
||||
@@ -60,9 +59,6 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
if (call == null) return null;
|
||||
|
||||
restoreComments(loopStatement, body);
|
||||
if (!tb.hasOperations() && StreamApiMigrationInspection.isAddAllCall(tb) && loopStatement instanceof PsiForeachStatement) {
|
||||
return handleAddAll(loopStatement, factory, call);
|
||||
}
|
||||
PsiExpression qualifierExpression = call.getMethodExpression().getQualifierExpression();
|
||||
PsiLocalVariable variable = StreamApiMigrationInspection.extractCollectionVariable(qualifierExpression);
|
||||
if(variable != null && InheritanceUtil.isInheritor(variable.getType(), CommonClassNames.JAVA_UTIL_MAP)) {
|
||||
@@ -158,19 +154,6 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
return replaceInitializer(loopStatement, variable, initializer, builder.toString(), status);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement handleAddAll(@NotNull PsiLoopStatement loopStatement, PsiElementFactory factory, PsiMethodCallExpression call) {
|
||||
PsiExpression iteratedValue = ((PsiForeachStatement)loopStatement).getIteratedValue();
|
||||
if (iteratedValue == null) return null;
|
||||
PsiExpression qualifierExpression = call.getMethodExpression().getQualifierExpression();
|
||||
String qualifierText = qualifierExpression != null ? qualifierExpression.getText() : "";
|
||||
String collectionText = iteratedValue.getType() instanceof PsiArrayType
|
||||
? CommonClassNames.JAVA_UTIL_ARRAYS + ".asList(" + iteratedValue.getText() + ")"
|
||||
: iteratedValue.getText();
|
||||
String callText = StringUtil.getQualifiedName(qualifierText, "addAll(" + collectionText + ");");
|
||||
return loopStatement.replace(factory.createStatementFromText(callText, loopStatement));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement handleToMap(@NotNull PsiLoopStatement loopStatement,
|
||||
@NotNull TerminalBlock tb,
|
||||
|
||||
+27
-18
@@ -140,6 +140,12 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
ref2.getQualifierExpression());
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts an addend from assignment expression like {@code x+=addend} or {@code x = x+addend}
|
||||
*
|
||||
* @param assignment assignment expression to extract an addend from
|
||||
* @return extracted addend expression or null if supplied assignment statement is not an addition
|
||||
*/
|
||||
@Nullable
|
||||
static PsiExpression extractAddend(PsiAssignmentExpression assignment) {
|
||||
if(JavaTokenType.PLUSEQ.equals(assignment.getOperationTokenType())) {
|
||||
@@ -184,6 +190,12 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract incremented value from expression which looks like {@code x++}, {@code ++x}, {@code x = x + 1} or {@code x += 1}
|
||||
*
|
||||
* @param expression expression to extract the incremented value
|
||||
* @return an extracted incremented value or null if increment pattern is not detected in the supplied expression
|
||||
*/
|
||||
@Contract("null -> null")
|
||||
static PsiExpression extractIncrementedLValue(PsiExpression expression) {
|
||||
if(expression instanceof PsiPostfixExpression) {
|
||||
@@ -675,23 +687,22 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
@Nullable
|
||||
private BaseStreamApiMigration findCollectMigration(PsiLoopStatement statement, TerminalBlock tb) {
|
||||
boolean addAll = statement instanceof PsiForeachStatement && !tb.hasOperations() && isAddAllCall(tb);
|
||||
// Don't suggest to convert the loop which can be trivially replaced via addAll:
|
||||
// this is covered by UseBulkOperationInspection and ManualArrayToCollectionCopyInspection
|
||||
if(addAll) return null;
|
||||
PsiMethodCallExpression call = tb.getSingleMethodCall();
|
||||
if(call != null && call.getMethodExpression().getQualifierExpression() instanceof PsiMethodCallExpression) {
|
||||
call = (PsiMethodCallExpression)call.getMethodExpression().getQualifierExpression();
|
||||
}
|
||||
String methodName;
|
||||
if(addAll) {
|
||||
methodName = "addAll";
|
||||
if(canCollect(statement, call)) {
|
||||
if(extractToArrayExpression(statement, call) != null)
|
||||
methodName = "toArray";
|
||||
else
|
||||
methodName = "collect";
|
||||
} else {
|
||||
PsiMethodCallExpression call = tb.getSingleMethodCall();
|
||||
if(call != null && call.getMethodExpression().getQualifierExpression() instanceof PsiMethodCallExpression) {
|
||||
call = (PsiMethodCallExpression)call.getMethodExpression().getQualifierExpression();
|
||||
}
|
||||
if(canCollect(statement, call)) {
|
||||
if(extractToArrayExpression(statement, call) != null)
|
||||
methodName = "toArray";
|
||||
else
|
||||
methodName = "collect";
|
||||
} else {
|
||||
if (!SUGGEST_FOREACH || tb.getCountExpression() != null) return null;
|
||||
methodName = "forEach";
|
||||
}
|
||||
if (!SUGGEST_FOREACH || tb.getCountExpression() != null) return null;
|
||||
methodName = "forEach";
|
||||
}
|
||||
return new CollectMigration(methodName);
|
||||
}
|
||||
@@ -1276,9 +1287,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
if(initializer == null) return null;
|
||||
|
||||
// check that increment is like for(...;...;i++)
|
||||
if(!(forStatement.getUpdate() instanceof PsiExpressionStatement)) return null;
|
||||
PsiExpression lValue = extractIncrementedLValue(((PsiExpressionStatement)forStatement.getUpdate()).getExpression());
|
||||
if(!ExpressionUtils.isReferenceTo(lValue, counter)) return null;
|
||||
if(!VariableAccessUtils.variableIsIncremented(counter, forStatement.getUpdate())) return null;
|
||||
|
||||
// check that condition is like for(...;i<bound;...) or for(...;i<=bound;...)
|
||||
if(!(forStatement.getCondition() instanceof PsiBinaryExpression)) return null;
|
||||
|
||||
+3
-2
@@ -1,12 +1,13 @@
|
||||
// "Replace with addAll" "true"
|
||||
// "Replace with collect" "false"
|
||||
import java.util.*;
|
||||
|
||||
public class Collect {
|
||||
class Person {}
|
||||
|
||||
void collectNames(List<Person> persons){
|
||||
// Handled by UseBulkOperationInspection
|
||||
List<Person> names = new ArrayList<>();
|
||||
for (Person person : pers<caret>ons) {
|
||||
for (Person person : p<caret>ersons) {
|
||||
names.add(person);
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
public void test(Integer[] arr) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
result.add(1);
|
||||
result.addAll(Arrays.asList(arr));
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Collect {
|
||||
class Person {}
|
||||
|
||||
void collectNames(List<Person> persons){
|
||||
List<Person> names = new ArrayList<>();
|
||||
names.addAll(persons);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace with addAll" "true"
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Collect {
|
||||
class Person {}
|
||||
|
||||
void collectNames(List<Person> persons){
|
||||
List<Person> names = new ArrayList<>();
|
||||
names.addAll(persons);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace with addAll" "true"
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace with addAll" "true"
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
public void test(Integer[] arr) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
result.add(1);
|
||||
result.addAll(Arrays.asList(arr));
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Main {
|
||||
public void test(Integer[] arr) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
result.add(1);
|
||||
result.addAll(Arrays.asList(arr));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// "Replace with addAll" "true"
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Collect {
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// "Replace iteration with bulk 'CrudRepository.save' call" "true"
|
||||
package org.springframework.data.repository;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
interface CrudRepository<T,ID extends Serializable> {
|
||||
<S extends T> Iterable<S> save(Iterable<S> entities);
|
||||
<S extends T> S save(S entity);
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public void test(CrudRepository<CharSequence, Integer> repo, Iterable<String> stringsToSave) {
|
||||
repo.save(stringsToSave);
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Replace iteration with bulk 'CrudRepository.save' call" "true"
|
||||
package org.springframework.data.repository;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
interface CrudRepository<T,ID extends Serializable> {
|
||||
<S extends T> Iterable<S> save(Iterable<S> entities);
|
||||
<S extends T> S save(S entity);
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public void test(CrudRepository<Iterable<String>, Integer> repo, Iterable<List<String>> stringsToSave) {
|
||||
repo.save(stringsToSave);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace iteration with bulk 'List.removeAll' call" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Collect {
|
||||
static class Person {}
|
||||
|
||||
void collectNames(List<Person> persons, Collection<Person> toRemove){
|
||||
persons.removeAll(toRemove);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace iteration with bulk 'List.removeAll' call" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Collect {
|
||||
static class Person {}
|
||||
|
||||
void collectNames(List<Person> persons, Collection<Person> toRemove){
|
||||
persons.removeAll(toRemove);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
public void test(Integer[] arr) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
result.add(1);
|
||||
Arrays.stream(arr).forEach(result<caret>::add);
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Collect {
|
||||
class Person {}
|
||||
|
||||
void collectNames(List<Person> persons){
|
||||
List<Person> names = new ArrayList<>();
|
||||
for(int i = 0; i<persons.size(); i = i + 1) {
|
||||
Person p = persons.get(i);
|
||||
names.<caret>add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
// "Replace with addAll" "false"
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "false"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -16,8 +16,8 @@ public class Main {
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends String> c) {
|
||||
for (String e : <caret>c) {
|
||||
add(e);
|
||||
for (String e : c) {
|
||||
a<caret>dd(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
// "Replace with addAll" "true"
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -15,8 +15,8 @@ public class Main {
|
||||
}
|
||||
|
||||
public boolean myAdd(Collection<? extends String> c) {
|
||||
for (String e : <caret>c) {
|
||||
this.add(e);
|
||||
for (String e : c) {
|
||||
this.<caret>add(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "false"
|
||||
import java.util.*;
|
||||
|
||||
public class Collect {
|
||||
class Person {}
|
||||
|
||||
void collectNames(Iterable<Person> persons){
|
||||
List<Person> names = new ArrayList<>();
|
||||
persons.forEach(p -> {
|
||||
names<caret>.add(p);
|
||||
});
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Collect {
|
||||
class Person {}
|
||||
|
||||
void collectNames(List<Person> persons){
|
||||
List<Person> names = new ArrayList<>();
|
||||
persons.forEach(p -> {
|
||||
names<caret>.add(p);
|
||||
});
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
// "Replace with addAll" "true"
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,8 +6,8 @@ class Sample {
|
||||
List<String> foo = new ArrayList<>();
|
||||
String foo(){
|
||||
Sample sm = new Sample();
|
||||
for (String s : fo<caret>o) {
|
||||
sm.foo.add(s);
|
||||
for (String s : foo) {
|
||||
<caret>sm.foo.add(s);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
// "Replace with addAll" "true"
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,7 +6,7 @@ public class Main {
|
||||
public void test(Integer[] arr) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
result.add(1);
|
||||
for(Integer i : ar<caret>r)
|
||||
result.add(i);
|
||||
for(Integer i : arr)
|
||||
result.<caret>add(i);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
public void test(Integer[] arr) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
result.add(1);
|
||||
for (int idx = 0; idx < arr.length; idx++) {
|
||||
result.<caret>add(arr[idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Main {
|
||||
public void test(Integer[] arr) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
result.add(1);
|
||||
Stream.of(arr).forEachOrdered(<caret>result::add);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Replace iteration with bulk 'Collection.addAll' call" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Collect {
|
||||
class Person {}
|
||||
|
||||
void collectNames(List<Person> persons){
|
||||
List<Person> names = new ArrayList<>();
|
||||
for (Person person : persons) {
|
||||
<caret>names.add(person);
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// "Replace iteration with bulk 'CrudRepository.save' call" "true"
|
||||
package org.springframework.data.repository;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
interface CrudRepository<T,ID extends Serializable> {
|
||||
<S extends T> Iterable<S> save(Iterable<S> entities);
|
||||
<S extends T> S save(S entity);
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public void test(CrudRepository<CharSequence, Integer> repo, Iterable<String> stringsToSave) {
|
||||
stringsToSave.forEach(<caret>repo::save);
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Replace iteration with bulk 'CrudRepository.save' call" "false"
|
||||
package org.springframework.data.repository;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
interface CrudRepository<T,ID extends Serializable> {
|
||||
<S extends T> Iterable<S> save(Iterable<S> entities);
|
||||
<S extends T> S save(S entity);
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public void test(CrudRepository<CharSequence, Integer> repo, Iterable<List<String>> stringsToSave) {
|
||||
stringsToSave.forEach(<caret>repo::save);
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Replace iteration with bulk 'CrudRepository.save' call" "true"
|
||||
package org.springframework.data.repository;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
interface CrudRepository<T,ID extends Serializable> {
|
||||
<S extends T> Iterable<S> save(Iterable<S> entities);
|
||||
<S extends T> S save(S entity);
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public void test(CrudRepository<Iterable<String>, Integer> repo, Iterable<List<String>> stringsToSave) {
|
||||
stringsToSave.forEach(<caret>repo::save);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace iteration with bulk 'List.removeAll' call" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Collect {
|
||||
static class Person {}
|
||||
|
||||
void collectNames(List<Person> persons, Collection<Person> toRemove){
|
||||
for (Iterator<Person> it = toRemove.iterator(); it.hasNext(); )
|
||||
persons<caret>.remove(it.next());
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Replace iteration with bulk 'List.removeAll' call" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Collect {
|
||||
static class Person {}
|
||||
|
||||
void collectNames(List<Person> persons, Collection<Person> toRemove){
|
||||
Iterator<Person> it = toRemove.iterator();
|
||||
while (it.hasNext()) {
|
||||
Person person = it.next();
|
||||
persons<caret>.remove(person);
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -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.codeInsight.daemon.quickFix;
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.UseBulkOperationInspection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class UseBulkOperationInspectionTest extends LightQuickFixParameterizedTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
UseBulkOperationInspection inspection = new UseBulkOperationInspection();
|
||||
inspection.USE_ARRAYS_AS_LIST = true;
|
||||
return new LocalInspectionTool[]{
|
||||
inspection
|
||||
};
|
||||
}
|
||||
|
||||
public void test() throws Exception { doAllTests(); }
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/useBulkOperation";
|
||||
}
|
||||
}
|
||||
@@ -752,4 +752,8 @@ inspection.lambda.to.method.call.fix.name=Replace lambda expression with ''{0}''
|
||||
inspection.module.exports.package.to.itself.message=Module exports package to itself
|
||||
inspection.module.exports.package.to.itself.only.message=Module exports package only to itself
|
||||
exports.to.itself.delete.module.fix.name=Delete reference to module ''{0}''
|
||||
exports.to.itself.delete.module.fix.family.name=Delete reference to module
|
||||
exports.to.itself.delete.module.fix.family.name=Delete reference to module
|
||||
|
||||
inspection.replace.with.bulk.message=Iteration can be replaced with bulk ''{0}'' call
|
||||
inspection.replace.with.bulk.fix.name=Replace iteration with bulk ''{0}'' call
|
||||
inspection.replace.with.bulk.fix.family.name=Replace with bulk method call
|
||||
|
||||
+5
-22
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -25,6 +25,7 @@ import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.BaseInspection;
|
||||
import com.siyeh.ig.BaseInspectionVisitor;
|
||||
import com.siyeh.ig.psiutils.*;
|
||||
import org.intellij.lang.annotations.Pattern;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -347,7 +348,7 @@ public class ForCanBeForeachInspectionBase extends BaseInspection {
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
if (!expressionIsArrayLengthLookup(referenceExpression)) {
|
||||
if (ExpressionUtils.getArrayFromLengthExpression(referenceExpression) == null) {
|
||||
final PsiElement target = referenceExpression.resolve();
|
||||
if (secondDeclaredElement != null && !secondDeclaredElement.equals(target)) {
|
||||
return null;
|
||||
@@ -366,7 +367,7 @@ public class ForCanBeForeachInspectionBase extends BaseInspection {
|
||||
return null;
|
||||
}
|
||||
referenceExpression = (PsiReferenceExpression)expression;
|
||||
if (!expressionIsArrayLengthLookup(referenceExpression)) {
|
||||
if (ExpressionUtils.getArrayFromLengthExpression(referenceExpression) == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -503,25 +504,7 @@ public class ForCanBeForeachInspectionBase extends BaseInspection {
|
||||
return new Holder(variable);
|
||||
}
|
||||
|
||||
private static boolean expressionIsArrayLengthLookup(PsiExpression expression) {
|
||||
expression = ParenthesesUtils.stripParentheses(expression);
|
||||
if (!(expression instanceof PsiReferenceExpression)) {
|
||||
return false;
|
||||
}
|
||||
final PsiReferenceExpression reference =
|
||||
(PsiReferenceExpression)expression;
|
||||
final String referenceName = reference.getReferenceName();
|
||||
if (!HardcodedMethodConstants.LENGTH.equals(referenceName)) {
|
||||
return false;
|
||||
}
|
||||
final PsiExpression qualifier = reference.getQualifierExpression();
|
||||
if (!(qualifier instanceof PsiReferenceExpression)) {
|
||||
return false;
|
||||
}
|
||||
final PsiType type = qualifier.getType();
|
||||
return type != null && type.getArrayDimensions() > 0;
|
||||
}
|
||||
|
||||
@Pattern(VALID_ID_PATTERN)
|
||||
@Override
|
||||
@NotNull
|
||||
public String getID() {
|
||||
|
||||
+21
@@ -25,6 +25,7 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.siyeh.HardcodedMethodConstants;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -864,4 +865,24 @@ public class ExpressionUtils {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array expression from array length retrieval expression
|
||||
* @param expression expression to extract an array expression from
|
||||
* @return an array expression or null if supplied expression is not array length retrieval
|
||||
*/
|
||||
@Nullable
|
||||
public static PsiExpression getArrayFromLengthExpression(PsiExpression expression) {
|
||||
expression = ParenthesesUtils.stripParentheses(expression);
|
||||
if (!(expression instanceof PsiReferenceExpression)) return null;
|
||||
final PsiReferenceExpression reference =
|
||||
(PsiReferenceExpression)expression;
|
||||
final String referenceName = reference.getReferenceName();
|
||||
if (!HardcodedMethodConstants.LENGTH.equals(referenceName)) return null;
|
||||
final PsiExpression qualifier = reference.getQualifierExpression();
|
||||
if (qualifier == null) return null;
|
||||
final PsiType type = qualifier.getType();
|
||||
if (type == null || type.getArrayDimensions() <= 0) return null;
|
||||
return qualifier;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection warns when calling some method in a loop (e.g. <code>collection.add(x)</code>) could be replaced when calling a bulk method
|
||||
(e.g. <code>collection.addAll(listOfX)</code>.
|
||||
<!-- tooltip end -->
|
||||
<p><small>New in 2017.1</small></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -858,6 +858,11 @@
|
||||
groupKey="group.names.declaration.redundancy" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.SimplifyStreamApiCallChainsInspection"
|
||||
displayName="Simplify stream API call chains"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="UseBulkOperation"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.performance.issues" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.UseBulkOperationInspection"
|
||||
displayName="Use bulk operation instead of iteration"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="ComparatorCombinators"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids" enabledByDefault="true" level="WARNING"
|
||||
|
||||
Reference in New Issue
Block a user