mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
RedundantStreamOptionalCall: warnings for single-element streams
IDEA-160435 Warn about parallelization of single-element stream IDEA-191569 Warn if stream of 0-1 elements is sorted or distincted
This commit is contained in:
+40
-5
@@ -26,6 +26,8 @@ import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
import static com.siyeh.ig.callMatcher.CallMatcher.anyOf;
|
||||
import static com.siyeh.ig.callMatcher.CallMatcher.staticCall;
|
||||
import static com.siyeh.ig.psiutils.StreamApiUtil.findSubsequentCall;
|
||||
|
||||
public class RedundantStreamOptionalCallInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
@@ -38,21 +40,36 @@ public class RedundantStreamOptionalCallInspection extends AbstractBaseJavaLocal
|
||||
private static final Set<String> CALLS_KEEPING_ELEMENTS_DISTINCT =
|
||||
ContainerUtil.set("filter", "boxed", "asLongStream", "limit", "skip", "sorted", "takeWhile", "dropWhile");
|
||||
private static final Set<String> CALLS_AFFECTING_PARALLELIZATION = ContainerUtil.set("sequential", "parallel");
|
||||
private static final Set<String> CALLS_USELESS_FOR_SINGLE_ELEMENT_STREAM = ContainerUtil.set("sorted", "distinct");
|
||||
private static final Set<String> BOX_UNBOX_NAMES = ContainerUtil
|
||||
.set("valueOf", "booleanValue", "byteValue", "charValue", "shortValue", "intValue", "longValue", "floatValue", "doubleValue");
|
||||
private static final Set<String> STANDARD_STREAM_INTERMEDIATE_OPERATIONS = ContainerUtil
|
||||
.set("asDoubleStream", "asLongStream", "boxed", "distinct", "dropWhile", "filter", "flatMap", "flatMapToDouble",
|
||||
"flatMapToInt", "flatMapToLong", "flatMapToObj", "limit", "map", "mapToDouble", "mapToInt", "mapToLong", "mapToObj", "onClose",
|
||||
"parallel", "peek", "sequential", "skip", "takeWhile", "unordered");
|
||||
private static final Set<String> STANDARD_STREAM_TERMINAL_OPERATIONS = ContainerUtil
|
||||
.set("allMatch", "anyMatch", "average", "collect", "count", "findAny", "findFirst", "forEach", "forEachOrdered", "max", "min",
|
||||
"noneMatch", "reduce", "sum", "summaryStatistics", "toArray");
|
||||
|
||||
private static final CallMatcher COLLECTOR_TO_SET =
|
||||
CallMatcher.staticCall(CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, "toSet", "toUnmodifiableSet").parameterCount(0);
|
||||
staticCall(CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, "toSet", "toUnmodifiableSet").parameterCount(0);
|
||||
private static final CallMatcher COLLECTOR_TO_COLLECTION =
|
||||
CallMatcher.staticCall(CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, "toCollection").parameterCount(1);
|
||||
staticCall(CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, "toCollection").parameterCount(1);
|
||||
private static final CallMatcher COLLECTOR_TO_MAP =
|
||||
CallMatcher.staticCall(CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, "toMap", "toUnmodifiableMap").parameterTypes(
|
||||
staticCall(CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS, "toMap", "toUnmodifiableMap").parameterTypes(
|
||||
CommonClassNames.JAVA_UTIL_FUNCTION_FUNCTION, CommonClassNames.JAVA_UTIL_FUNCTION_FUNCTION);
|
||||
private static final CallMatcher UNORDERED_COLLECTORS = CallMatcher.anyOf(COLLECTOR_TO_MAP, COLLECTOR_TO_SET);
|
||||
private static final CallMatcher UNORDERED_COLLECTORS = anyOf(COLLECTOR_TO_MAP, COLLECTOR_TO_SET);
|
||||
private static final Predicate<PsiMethodCallExpression> UNORDERED_COLLECTOR =
|
||||
UNORDERED_COLLECTORS.or(RedundantStreamOptionalCallInspection::isUnorderedToCollection);
|
||||
private static final Set<String> SET_CLASSES =
|
||||
ImmutableSet.of(CommonClassNames.JAVA_UTIL_HASH_SET, "java.util.LinkedHashSet", "java.util.TreeSet");
|
||||
private static final CallMatcher STREAM_OF_SINGLE =
|
||||
anyOf(
|
||||
staticCall(CommonClassNames.JAVA_UTIL_STREAM_STREAM, "of").parameterTypes("T"),
|
||||
staticCall(CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM, "of").parameterTypes("int"),
|
||||
staticCall(CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM, "of").parameterTypes("long"),
|
||||
staticCall(CommonClassNames.JAVA_UTIL_STREAM_DOUBLE_STREAM, "of").parameterTypes("double")
|
||||
);
|
||||
|
||||
@SuppressWarnings("PublicField")
|
||||
public boolean USELESS_BOXING_IN_STREAM_MAP = true;
|
||||
@@ -73,6 +90,9 @@ public class RedundantStreamOptionalCallInspection extends AbstractBaseJavaLocal
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression call) {
|
||||
if (STREAM_OF_SINGLE.test(call)) {
|
||||
handleSingleElementStream(call);
|
||||
}
|
||||
PsiReferenceExpression methodExpression = call.getMethodExpression();
|
||||
String name = methodExpression.getReferenceName();
|
||||
if (name == null || !INTERESTING_NAMES.contains(name)) return;
|
||||
@@ -166,6 +186,21 @@ public class RedundantStreamOptionalCallInspection extends AbstractBaseJavaLocal
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSingleElementStream(PsiMethodCallExpression call) {
|
||||
PsiMethodCallExpression subsequentCall =
|
||||
findSubsequentCall(call, CALLS_USELESS_FOR_SINGLE_ELEMENT_STREAM::contains,
|
||||
name -> STANDARD_STREAM_INTERMEDIATE_OPERATIONS.contains(name) && !name.startsWith("flatMap"));
|
||||
if (subsequentCall != null) {
|
||||
register(subsequentCall, InspectionsBundle.message("inspection.redundant.stream.optional.call.explanation.at.most.one"));
|
||||
return;
|
||||
}
|
||||
Predicate<String> standardNoSorted = name -> STANDARD_STREAM_INTERMEDIATE_OPERATIONS.contains(name) && !name.equals("sorted");
|
||||
PsiMethodCallExpression parallelCall = findSubsequentCall(call, "parallel"::equals, standardNoSorted);
|
||||
if (parallelCall != null && findSubsequentCall(call, STANDARD_STREAM_TERMINAL_OPERATIONS::contains, standardNoSorted) != null) {
|
||||
register(parallelCall, InspectionsBundle.message("inspection.redundant.stream.optional.call.explanation.parallel.single"));
|
||||
}
|
||||
}
|
||||
|
||||
private void register(PsiMethodCallExpression call, String explanation, LocalQuickFix... additionalFixes) {
|
||||
String methodName = call.getMethodExpression().getReferenceName();
|
||||
String message = InspectionsBundle.message("inspection.redundant.stream.optional.call.message", methodName);
|
||||
@@ -291,7 +326,7 @@ public class RedundantStreamOptionalCallInspection extends AbstractBaseJavaLocal
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Collect to 'LinkedHashSet'";
|
||||
return InspectionsBundle.message("inspection.redundant.stream.optional.call.fix.collect.to.ordered.family.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-1
@@ -37,7 +37,7 @@ public class RedundantStreamOptionalCall {
|
||||
.filter(x -> x > 0).distinct().sequential().forEach(System.out::println);
|
||||
Stream.of(0, 100).map(x -> x*2).<warning descr="Redundant 'sequential' call: there's subsequent 'parallel' call which overrides this call">sequential()</warning>
|
||||
.filter(x -> x > 0).limit(10).parallel().forEach(System.out::println);
|
||||
Stream.of("xyz").parallel().sorted().collect(Collectors.toList()).stream().sequential().forEach(System.out::println);
|
||||
Stream.of("xyz").parallel().<warning descr="Redundant 'sorted' call: stream contains at most one element">sorted()</warning>.collect(Collectors.toList()).stream().sequential().forEach(System.out::println);
|
||||
|
||||
IntStream.range(0, 100).unordered().filter(x -> x > 50).<warning descr="Redundant 'unordered' call: there already was an 'unordered' call in the chain">unordered()</warning>.forEach(System.out::println);
|
||||
IntStream.range(0, 100).unordered().filter(x -> x > 50).sorted().unordered().forEach(System.out::println);
|
||||
@@ -57,5 +57,11 @@ public class RedundantStreamOptionalCall {
|
||||
Set<String> set7 = collection.stream().<warning descr="Redundant 'distinct' call: elements will be distinct anyways when collected to the Set">distinct()</warning>.collect(Collectors.toCollection(HashSet::new));
|
||||
Set<String> set8 = collection.stream().<warning descr="Redundant 'distinct' call: elements will be distinct anyways when collected to the Set">distinct()</warning>.collect(Collectors.toCollection(() -> new HashSet<>()));
|
||||
Set<String> set8a = collection.stream().<warning descr="Redundant 'distinct' call: elements will be distinct anyways when collected to the Set">distinct()</warning>.collect(Collectors.toCollection(() -> new LinkedHashSet<>()));
|
||||
|
||||
IntStream.of(123).mapToObj(String::valueOf).<warning descr="Redundant 'sorted' call: stream contains at most one element">sorted()</warning>;
|
||||
LongStream.of(123).filter(x -> x > 0).mapToObj(String::valueOf).<warning descr="Redundant 'distinct' call: stream contains at most one element">distinct()</warning>;
|
||||
LongStream.of(123).filter(x -> x > 0).mapToObj(String::valueOf).flatMap(x -> Stream.of(x, x+x)).distinct();
|
||||
Stream.of("foo").flatMap(x -> Stream.of(x, x)).parallel();
|
||||
Stream.of("foo").flatMap(x -> Stream.of(x, x)).<warning descr="Redundant 'parallel' call: stream created from single element will not be parallelized">parallel()</warning>.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,8 +831,11 @@ inspection.redundant.stream.optional.call.explanation.distinct=there already was
|
||||
inspection.redundant.stream.optional.call.explanation.distinct.set=elements will be distinct anyways when collected to the Set
|
||||
inspection.redundant.stream.optional.call.explanation.unordered=there already was an 'unordered' call in the chain
|
||||
inspection.redundant.stream.optional.call.explanation.parallel=there''s subsequent ''{0}'' call which overrides this call
|
||||
inspection.redundant.stream.optional.call.explanation.at.most.one=stream contains at most one element
|
||||
inspection.redundant.stream.optional.call.explanation.parallel.single=stream created from single element will not be parallelized
|
||||
inspection.redundant.stream.optional.call.fix.family.name=Remove redundant chain call
|
||||
inspection.redundant.stream.optional.call.fix.name=Remove ''{0}'' call
|
||||
inspection.redundant.stream.optional.call.fix.collect.to.ordered.family.name=Collect to 'LinkedHashSet'
|
||||
inspection.redundant.stream.optional.call.option.streamboxing=Report useless boxing in Stream.map
|
||||
|
||||
inspection.map.foreach.message=Can be replaced with 'Map.forEach'
|
||||
|
||||
Reference in New Issue
Block a user