CollectionUtils#isCollectionOrMapSize

Used in ToArrayCallWithZeroLengthArrayArgumentInspection and StreamApiMigrationInspection
This commit is contained in:
Tagir Valeev
2018-01-30 11:01:38 +07:00
parent abd1458307
commit 3210510c96
9 changed files with 114 additions and 28 deletions
@@ -299,11 +299,7 @@ class CollectMigration extends BaseStreamApiMigration {
AddingTerminal terminal = new AddingTerminal(variable, tb.getVariable(), call, tb.getStreamSourceStatement(), status);
if (count == null) return terminal;
// like "list.add(x); if(list.size() >= limit) break;"
if (!(count instanceof PsiMethodCallExpression)) return null;
PsiMethodCallExpression sizeCall = (PsiMethodCallExpression)count;
PsiExpression sizeQualifier = sizeCall.getMethodExpression().getQualifierExpression();
if (isCallOf(sizeCall, CommonClassNames.JAVA_UTIL_COLLECTION, "size") &&
EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(sizeQualifier, qualifierExpression) &&
if (CollectionUtils.isCollectionOrMapSize(count, qualifierExpression) &&
InheritanceUtil.isInheritor(PsiUtil.resolveClassInClassTypeOnly(variable.getType()), CommonClassNames.JAVA_UTIL_LIST)) {
return terminal;
}
@@ -824,13 +820,13 @@ class CollectMigration extends BaseStreamApiMigration {
return null;
}
PsiLocalVariable var = tryCast(PsiUtil.skipParenthesizedExprUp(toArrayCandidate.getParent()), PsiLocalVariable.class);
String supplier = extractSupplier(toArrayCandidate, terminal);
String supplier = extractSupplier(toArrayCandidate);
if (supplier == null) return null;
return new ToArrayTerminal(terminal, var, intermediateSteps, toArrayCandidate, supplier);
}
@Nullable
static String extractSupplier(PsiMethodCallExpression toArrayCandidate, CollectTerminal terminal) {
static String extractSupplier(PsiMethodCallExpression toArrayCandidate) {
// collection.toArray() or collection.toArray(new Type[0]) or collection.toArray(new Type[collection.size()]);
PsiExpression[] args = toArrayCandidate.getArgumentList().getExpressions();
if (args.length == 0) return "";
@@ -841,13 +837,12 @@ class CollectMigration extends BaseStreamApiMigration {
String name = arrayType.getCanonicalText();
PsiExpression[] dimensions = newArray.getArrayDimensions();
if (dimensions.length != 1) return null;
if (ExpressionUtils.isZero(dimensions[0])) return name+"::new";
if (!(dimensions[0] instanceof PsiMethodCallExpression)) return null;
PsiMethodCallExpression maybeSizeCall = (PsiMethodCallExpression)dimensions[0];
if (!isCallOf(maybeSizeCall, CommonClassNames.JAVA_UTIL_COLLECTION, "size")) return null;
PsiExpression sizeQualifier = maybeSizeCall.getMethodExpression().getQualifierExpression();
if (!terminal.isTargetReference(sizeQualifier)) return null;
return name+"::new";
PsiExpression qualifier = toArrayCandidate.getMethodExpression().getQualifierExpression();
if (ExpressionUtils.isZero(dimensions[0]) ||
(qualifier != null && CollectionUtils.isCollectionOrMapSize(dimensions[0], qualifier))) {
return name + "::new";
}
return null;
}
}
@@ -631,14 +631,25 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
PsiExpression dimension = ArrayUtil.getFirstElement(initializer.getArrayDimensions());
if (dimension == null) return null;
PsiExpression bound = loop.myBound;
if (!PsiEquivalenceUtil.areElementsEquivalent(dimension, bound) &&
!ExpressionUtils.isReferenceTo(ExpressionUtils.getArrayFromLengthExpression(bound), arrayVariable)) {
return null;
}
if (!isArrayLength(arrayVariable, dimension, bound)) return null;
if (VariableAccessUtils.variableIsUsed(arrayVariable, assignment.getRExpression())) return null;
return arrayVariable;
}
private static boolean isArrayLength(PsiLocalVariable arrayVariable, PsiExpression dimension, PsiExpression bound) {
if (ExpressionUtils.isReferenceTo(ExpressionUtils.getArrayFromLengthExpression(bound), arrayVariable)) return true;
if (PsiEquivalenceUtil.areElementsEquivalent(dimension, bound)) return true;
if (bound instanceof PsiMethodCallExpression) {
PsiExpression qualifier = ((PsiMethodCallExpression)bound).getMethodExpression().getQualifierExpression();
if (qualifier != null && CollectionUtils.isCollectionOrMapSize(dimension, qualifier)) return true;
}
if (dimension instanceof PsiMethodCallExpression) {
PsiExpression qualifier = ((PsiMethodCallExpression)dimension).getMethodExpression().getQualifierExpression();
if (qualifier != null && CollectionUtils.isCollectionOrMapSize(bound, qualifier)) return true;
}
return false;
}
/**
* Intermediate stream operation representation
*/
@@ -0,0 +1,10 @@
// "Replace with toArray" "true"
import java.util.*;
import java.util.stream.IntStream;
public class Main {
public String[] test(Map<String, String> map) {
String[] array = IntStream.range(0, map.size()).mapToObj(String::valueOf).toArray(String[]::new);
return array;
}
}
@@ -0,0 +1,12 @@
// "Replace with toArray" "true"
import java.util.*;
public class Main {
public String[] test(Map<String, String> map) {
String[] array = new String[map.size()];
for(int<caret> i=0; i<map.keySet().size(); i++) {
array[i] = String.valueOf(i);
}
return array;
}
}
@@ -15,9 +15,12 @@
*/
package com.siyeh.ig.psiutils;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.callMatcher.CallMatcher;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -26,6 +29,16 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
public class CollectionUtils {
private static final CallMatcher COLLECTION_MAP_SIZE =
CallMatcher.anyOf(
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_COLLECTION, "size").parameterCount(0),
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_MAP, "size").parameterCount(0));
private static final CallMatcher DERIVED_COLLECTION =
CallMatcher.anyOf(
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_MAP, "keySet", "values", "entrySet").parameterCount(0),
CallMatcher.instanceCall("java.util.NavigableMap", "descendingKeySet", "descendingMap", "navigableKeySet").parameterCount(0),
CallMatcher.instanceCall("java.util.NavigableSet", "descendingSet").parameterCount(0)
);
/**
* @noinspection StaticCollection
@@ -244,4 +257,36 @@ public class CollectionUtils {
}
return s_interfaceForCollection.get(baseName);
}
/**
* Checks whether supplied expression represents a collection size. It handles some derived collections,
* e.g. it's known that {@code map.size()} is the size of {@code map.keySet()}.
*
* @param expression expression to test
* @param collection expected collection or map expression
* @return true if the supplied expression represents a collection size
*/
@Contract("null, _ -> false")
public static boolean isCollectionOrMapSize(@Nullable PsiExpression expression, @NotNull PsiExpression collection) {
PsiMethodCallExpression sizeCall = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(expression), PsiMethodCallExpression.class);
if (!COLLECTION_MAP_SIZE.test(sizeCall)) return false;
PsiExpression sizeQualifier = sizeCall.getMethodExpression().getQualifierExpression();
if (sizeQualifier == null) return false;
sizeQualifier = getBaseCollection(sizeQualifier);
collection = getBaseCollection(collection);
return PsiEquivalenceUtil.areElementsEquivalent(sizeQualifier, collection);
}
private static @NotNull PsiExpression getBaseCollection(@NotNull PsiExpression derivedCollection) {
while(true) {
PsiMethodCallExpression derivedCall =
ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(derivedCollection), PsiMethodCallExpression.class);
if (DERIVED_COLLECTION.test(derivedCall)) {
derivedCollection = ExpressionUtils.getQualifierOrThis(derivedCall.getMethodExpression());
}
else {
return derivedCollection;
}
}
}
}
@@ -15,7 +15,6 @@
*/
package com.siyeh.ig.performance;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.VerticalFlowLayout;
@@ -34,8 +33,6 @@ import org.jetbrains.annotations.*;
import javax.swing.*;
public class ToArrayCallWithZeroLengthArrayArgumentInspection extends BaseInspection {
private static final CallMatcher COLLECTION_SIZE =
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_COLLECTION, "size").parameterCount(0);
private static final CallMatcher COLLECTION_TO_ARRAY =
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_COLLECTION, "toArray").parameterCount(1);
@@ -143,13 +140,7 @@ public class ToArrayCallWithZeroLengthArrayArgumentInspection extends BaseInspec
if (newExpression == null) return false;
PsiExpression[] dimensions = newExpression.getArrayDimensions();
if (dimensions.length != 1) return false;
PsiMethodCallExpression maybeSizeCall =
ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(dimensions[0]), PsiMethodCallExpression.class);
if (COLLECTION_SIZE.test(maybeSizeCall)) {
PsiExpression sizeQualifier = maybeSizeCall.getMethodExpression().getQualifierExpression();
return sizeQualifier != null && PsiEquivalenceUtil.areElementsEquivalent(sizeQualifier, qualifier);
}
return false;
return CollectionUtils.isCollectionOrMapSize(dimensions[0], qualifier);
}
private static class ToArrayCallWithZeroLengthArrayArgumentFix extends InspectionGadgetsFix {
@@ -0,0 +1,9 @@
package com.siyeh.igfixes.performance.to_array_call_with_zero_length_array_argument;
import java.util.Map;
class IntroduceVariable {
String[] keys(Map<String, String> map) {
return map.keySet().toArray(new String[0]);
}
}
@@ -0,0 +1,9 @@
package com.siyeh.igfixes.performance.to_array_call_with_zero_length_array_argument;
import java.util.Map;
class IntroduceVariable {
String[] keys(Map<String, String> map) {
return map.keySet().toAr<caret>ray(new String[map.size()]);
}
}
@@ -37,6 +37,10 @@ public class ToArrayCallWithZeroLengthArrayArgumentFixTest extends IGQuickFixesT
doFixTest(PreferEmptyArray.ALWAYS, InspectionGadgetsBundle.message("to.array.call.style.quickfix.make.zero"));
}
public void testPresizedToZeroMap() {
doFixTest(PreferEmptyArray.ALWAYS, InspectionGadgetsBundle.message("to.array.call.style.quickfix.make.zero"));
}
private void doFixTest(PreferEmptyArray mode, String message) {
myInspection.myMode = mode;
doTest(getTestName(false), message);