mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
extract simplify forEach inspection: IDEA-CR-24052
This commit is contained in:
+18
-12
@@ -28,7 +28,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
abstract class BaseStreamApiMigration {
|
||||
private final boolean myShouldWarn;
|
||||
private boolean myShouldWarn;
|
||||
private final String myReplacement;
|
||||
|
||||
protected BaseStreamApiMigration(boolean shouldWarn, String replacement) {
|
||||
@@ -40,19 +40,23 @@ abstract class BaseStreamApiMigration {
|
||||
return myReplacement;
|
||||
}
|
||||
|
||||
abstract PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb);
|
||||
abstract PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb);
|
||||
|
||||
public boolean isShouldWarn() {
|
||||
return myShouldWarn;
|
||||
}
|
||||
|
||||
static PsiElement replaceWithOperation(PsiLoopStatement loopStatement,
|
||||
public void setShouldWarn(boolean shouldWarn) {
|
||||
myShouldWarn = shouldWarn;
|
||||
}
|
||||
|
||||
static PsiElement replaceWithOperation(PsiStatement loopStatement,
|
||||
PsiVariable var,
|
||||
String streamText,
|
||||
PsiType expressionType,
|
||||
OperationReductionMigration.ReductionOperation reductionOperation) {
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(loopStatement.getProject());
|
||||
restoreComments(loopStatement, loopStatement.getBody());
|
||||
restoreComments(loopStatement, loopStatement instanceof PsiLoopStatement? ((PsiLoopStatement)loopStatement).getBody(): loopStatement);
|
||||
InitializerUsageStatus status = ControlFlowUtils.getInitializerUsageStatus(var, loopStatement);
|
||||
if (status != InitializerUsageStatus.UNKNOWN) {
|
||||
PsiExpression initializer = var.getInitializer();
|
||||
@@ -67,7 +71,7 @@ abstract class BaseStreamApiMigration {
|
||||
loopStatement));
|
||||
}
|
||||
|
||||
static PsiElement replaceInitializer(PsiLoopStatement loopStatement,
|
||||
static PsiElement replaceInitializer(PsiStatement loopStatement,
|
||||
PsiVariable var,
|
||||
PsiExpression initializer,
|
||||
String replacement,
|
||||
@@ -90,11 +94,11 @@ abstract class BaseStreamApiMigration {
|
||||
|
||||
|
||||
@Nullable
|
||||
static PsiElement replaceWithFindExtremum(@NotNull PsiLoopStatement loopStatement,
|
||||
static PsiElement replaceWithFindExtremum(@NotNull PsiStatement loopStatement,
|
||||
@NotNull PsiVariable extremumHolder,
|
||||
@NotNull String streamText,
|
||||
@Nullable PsiVariable keyExtremum) {
|
||||
restoreComments(loopStatement, loopStatement.getBody());
|
||||
restoreComments(loopStatement, loopStatement instanceof PsiLoopStatement? ((PsiLoopStatement)loopStatement).getBody(): loopStatement);
|
||||
if(keyExtremum != null) {
|
||||
keyExtremum.delete();
|
||||
}
|
||||
@@ -102,14 +106,16 @@ abstract class BaseStreamApiMigration {
|
||||
return replaceInitializer(loopStatement, extremumHolder, extremumHolder.getInitializer(), streamText, status);
|
||||
}
|
||||
|
||||
static void restoreComments(PsiLoopStatement loopStatement, PsiStatement body) {
|
||||
final PsiElement parent = loopStatement.getParent();
|
||||
for (PsiElement comment : PsiTreeUtil.findChildrenOfType(body, PsiComment.class)) {
|
||||
parent.addBefore(comment, loopStatement);
|
||||
static void restoreComments(PsiStatement statement, PsiElement body) {
|
||||
if(statement instanceof PsiLoopStatement || statement instanceof PsiExpressionStatement) {
|
||||
final PsiElement parent = statement.getParent();
|
||||
for (PsiElement comment : PsiTreeUtil.findChildrenOfType(body, PsiComment.class)) {
|
||||
parent.addBefore(comment, statement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void removeLoop(@NotNull PsiLoopStatement statement) {
|
||||
static void removeLoop(@NotNull PsiStatement statement) {
|
||||
PsiElement parent = statement.getParent();
|
||||
if (parent instanceof PsiLabeledStatement) {
|
||||
parent.delete();
|
||||
|
||||
+16
-16
@@ -75,8 +75,8 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
@Override
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) {
|
||||
PsiLoopStatement loopStatement = tb.getMainLoop();
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
|
||||
PsiStatement loopStatement = tb.getStreamSourceStatement();
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
CollectTerminal terminal = extractCollectTerminal(tb, null);
|
||||
if (terminal == null) return null;
|
||||
@@ -139,12 +139,12 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
CollectTerminal terminal = StreamEx.of(extractors).map(extractor -> extractor.apply(tb, call)).nonNull().findFirst().orElse(null);
|
||||
if (terminal != null) {
|
||||
if (terminal.getStatus() == ControlFlowUtils.InitializerUsageStatus.UNKNOWN) return null;
|
||||
terminal = includePostStatements(terminal, tb.getMainLoop());
|
||||
terminal = includePostStatements(terminal, tb.getStreamSourceStatement());
|
||||
}
|
||||
return terminal;
|
||||
}
|
||||
|
||||
static CollectTerminal includePostStatements(CollectTerminal terminal, PsiLoopStatement loop) {
|
||||
static CollectTerminal includePostStatements(CollectTerminal terminal, PsiStatement loop) {
|
||||
List<BiFunction<CollectTerminal, PsiElement, CollectTerminal>> wrappers =
|
||||
Arrays.asList(SortingTerminal::tryWrap, ToArrayTerminal::tryWrap, NewListTerminal::tryWrap);
|
||||
PsiElement nextStatement = loop;
|
||||
@@ -194,9 +194,9 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
abstract static class CollectTerminal {
|
||||
private final PsiLocalVariable myTargetVariable;
|
||||
private final InitializerUsageStatus myStatus;
|
||||
final PsiLoopStatement myLoop;
|
||||
final PsiStatement myLoop;
|
||||
|
||||
protected CollectTerminal(PsiLocalVariable variable, PsiLoopStatement loop, InitializerUsageStatus status) {
|
||||
protected CollectTerminal(PsiLocalVariable variable, PsiStatement loop, InitializerUsageStatus status) {
|
||||
myTargetVariable = variable;
|
||||
myLoop = loop;
|
||||
myStatus = status;
|
||||
@@ -235,7 +235,7 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
AddingTerminal(@NotNull PsiLocalVariable target,
|
||||
PsiVariable element,
|
||||
PsiMethodCallExpression addCall,
|
||||
PsiLoopStatement loop,
|
||||
PsiStatement loop,
|
||||
InitializerUsageStatus status) {
|
||||
super(target, loop, hasLambdaCompatibleEmptyInitializer(target) ? status : ControlFlowUtils.InitializerUsageStatus.UNKNOWN);
|
||||
myTargetType = target.getType();
|
||||
@@ -288,8 +288,8 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
PsiExpression count = tb.getCountExpression();
|
||||
PsiLocalVariable variable = extractQualifierVariable(tb, call);
|
||||
if (variable != null) {
|
||||
InitializerUsageStatus status = getInitializerUsageStatus(variable, tb.getMainLoop());
|
||||
AddingTerminal terminal = new AddingTerminal(variable, tb.getVariable(), call, tb.getMainLoop(), status);
|
||||
InitializerUsageStatus status = getInitializerUsageStatus(variable, tb.getStreamSourceStatement());
|
||||
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;
|
||||
@@ -346,7 +346,7 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
AddingAllTerminal(PsiLocalVariable target,
|
||||
PsiVariable element,
|
||||
PsiMethodCallExpression addAllCall,
|
||||
PsiLoopStatement loop,
|
||||
PsiStatement loop,
|
||||
InitializerUsageStatus status) {
|
||||
super(target, element, null, loop, status);
|
||||
myAddAllCall = addAllCall;
|
||||
@@ -380,8 +380,8 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
if (collectionReference == null || tb.dependsOn(collectionReference)) return null;
|
||||
PsiLocalVariable target = tryCast(collectionReference.resolve(), PsiLocalVariable.class);
|
||||
if (target == null || StreamEx.of(args).skip(1).anyMatch(arg -> VariableAccessUtils.variableIsUsed(target, arg))) return null;
|
||||
InitializerUsageStatus status = getInitializerUsageStatus(target, tb.getMainLoop());
|
||||
return new AddingAllTerminal(target, tb.getVariable(), call, tb.getMainLoop(), status);
|
||||
InitializerUsageStatus status = getInitializerUsageStatus(target, tb.getStreamSourceStatement());
|
||||
return new AddingAllTerminal(target, tb.getVariable(), call, tb.getStreamSourceStatement(), status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,7 +447,7 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
PsiType valueType = PsiUtil.substituteTypeParameter(mapType, CommonClassNames.JAVA_UTIL_MAP, 1, false);
|
||||
if (valueType == null) return null;
|
||||
AddingTerminal adding = new AddingTerminal(valueType, body, tb.getVariable(), call);
|
||||
InitializerUsageStatus status = getInitializerUsageStatus(variable, tb.getMainLoop());
|
||||
InitializerUsageStatus status = getInitializerUsageStatus(variable, tb.getStreamSourceStatement());
|
||||
return new GroupingTerminal(adding, variable, args[0], status);
|
||||
}
|
||||
}
|
||||
@@ -464,7 +464,7 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
ToMapTerminal(PsiMethodCallExpression call,
|
||||
PsiVariable elementVariable,
|
||||
PsiLocalVariable variable,
|
||||
PsiLoopStatement loop,
|
||||
PsiStatement loop,
|
||||
InitializerUsageStatus status) {
|
||||
super(variable, loop, status);
|
||||
myMapUpdateCall = call;
|
||||
@@ -517,8 +517,8 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
PsiLocalVariable variable = extractQualifierVariable(tb, call);
|
||||
if (!hasLambdaCompatibleEmptyInitializer(variable)) return null;
|
||||
InitializerUsageStatus status = getInitializerUsageStatus(variable, tb.getMainLoop());
|
||||
return new ToMapTerminal(call, tb.getVariable(), variable, tb.getMainLoop(), status);
|
||||
InitializerUsageStatus status = getInitializerUsageStatus(variable, tb.getStreamSourceStatement());
|
||||
return new ToMapTerminal(call, tb.getVariable(), variable, tb.getStreamSourceStatement(), status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class CountMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
@Override
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) {
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
|
||||
PsiExpression expression = tb.getSingleExpression(PsiExpression.class);
|
||||
if (expression == null) {
|
||||
expression = tb.getCountExpression();
|
||||
@@ -41,6 +41,6 @@ class CountMigration extends BaseStreamApiMigration {
|
||||
PsiElement element = ((PsiReferenceExpression)operand).resolve();
|
||||
if (!(element instanceof PsiLocalVariable)) return null;
|
||||
PsiLocalVariable var = (PsiLocalVariable)element;
|
||||
return replaceWithOperation(tb.getMainLoop(), var, tb.generate() + ".count()", PsiType.LONG, SUM_OPERATION);
|
||||
return replaceWithOperation(tb.getStreamSourceStatement(), var, tb.generate() + ".count()", PsiType.LONG, SUM_OPERATION);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -46,7 +46,7 @@ class FindExtremumMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
@Override
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) {
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
|
||||
ExtremumTerminal terminal = extract(tb, null);
|
||||
if (terminal == null) return null;
|
||||
return terminal.replace();
|
||||
@@ -187,7 +187,7 @@ class FindExtremumMigration extends BaseStreamApiMigration {
|
||||
static private boolean mayChangeBeforeLoop(@NotNull PsiVariable variable,
|
||||
@NotNull TerminalBlock terminalBlock) {
|
||||
ControlFlowUtils.InitializerUsageStatus status =
|
||||
ControlFlowUtils.getInitializerUsageStatus(variable, terminalBlock.getMainLoop());
|
||||
ControlFlowUtils.getInitializerUsageStatus(variable, terminalBlock.getStreamSourceStatement());
|
||||
return status.equals(ControlFlowUtils.InitializerUsageStatus.UNKNOWN);
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ class FindExtremumMigration extends BaseStreamApiMigration {
|
||||
if (method == null) return null;
|
||||
|
||||
String inFilterOperation = myMax ? ">=" : "<=";
|
||||
PsiLoopStatement loop = myTerminalBlock.getMainLoop();
|
||||
PsiStatement loop = myTerminalBlock.getStreamSourceStatement();
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(loop.getProject());
|
||||
String extremumInitializer = myExtremumKeyInitializer.getText();
|
||||
PsiExpression condition =
|
||||
@@ -304,7 +304,7 @@ class FindExtremumMigration extends BaseStreamApiMigration {
|
||||
comparator = comparatorName;
|
||||
}
|
||||
String stream = blockWithFilter.generate() + "." + getOperation(myMax) + "(" + comparator + ").orElse(null)";
|
||||
return replaceWithFindExtremum(myTerminalBlock.getMainLoop(), myExtremum, stream, myExtremumKey);
|
||||
return replaceWithFindExtremum(myTerminalBlock.getStreamSourceStatement(), myExtremum, stream, myExtremumKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -473,7 +473,7 @@ class FindExtremumMigration extends BaseStreamApiMigration {
|
||||
terminalBlock = blockWithMap;
|
||||
}
|
||||
String inFilterOperation = myMax ? ">=" : "<=";
|
||||
PsiLoopStatement loop = terminalBlock.getMainLoop();
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(loop.getProject());
|
||||
String extremumInitializer = myExtremumInitializer.getText();
|
||||
Object nonFilterableInitialValue = getNonFilterableInitialValue(type, myMax);
|
||||
@@ -573,7 +573,7 @@ class FindExtremumMigration extends BaseStreamApiMigration {
|
||||
comparator = comparatorName;
|
||||
}
|
||||
String stream = myTerminalBlock.generate() + "." + getOperation(myMax) + "(" + comparator + ").orElse(null)";
|
||||
return replaceWithFindExtremum(myTerminalBlock.getMainLoop(), myExtremum, stream, null);
|
||||
return replaceWithFindExtremum(myTerminalBlock.getStreamSourceStatement(), myExtremum, stream, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -34,10 +34,10 @@ class FindFirstMigration extends BaseStreamApiMigration {
|
||||
FindFirstMigration(boolean shouldWarn) {super(shouldWarn, "findFirst()");}
|
||||
|
||||
@Override
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) {
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
|
||||
PsiStatement statement = tb.getSingleStatement();
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
|
||||
PsiLoopStatement loopStatement = tb.getMainLoop();
|
||||
PsiStatement loopStatement = tb.getStreamSourceStatement();
|
||||
if (statement instanceof PsiReturnStatement) {
|
||||
PsiReturnStatement returnStatement = (PsiReturnStatement)statement;
|
||||
PsiExpression value = returnStatement.getReturnValue();
|
||||
|
||||
+2
-2
@@ -57,8 +57,8 @@ class ForEachMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
@Override
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) {
|
||||
PsiLoopStatement loopStatement = tb.getMainLoop();
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
|
||||
PsiStatement loopStatement = tb.getStreamSourceStatement();
|
||||
restoreComments(loopStatement, body);
|
||||
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
|
||||
+35
-15
@@ -26,6 +26,7 @@ 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.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
@@ -37,6 +38,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil.isEffectivelyFinal;
|
||||
@@ -53,12 +55,12 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
@Override
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) {
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
|
||||
JoiningTerminal terminal = extractTerminal(tb, null);
|
||||
if(terminal == null) return null;
|
||||
|
||||
TerminalBlock block = terminal.getTerminalBlock();
|
||||
PsiLoopStatement loopStatement = block.getMainLoop();
|
||||
PsiStatement loopStatement = block.getStreamSourceStatement();
|
||||
String stream = terminal.generateStreamCode();
|
||||
restoreComments(loopStatement, body);
|
||||
PsiLocalVariable builder = terminal.getBuilder();
|
||||
@@ -171,10 +173,22 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canBeMadeNonFinal(@NotNull PsiLocalVariable variable, @NotNull PsiLoopStatement loop) {
|
||||
NavigatablePsiElement loopBound = PsiTreeUtil.getParentOfType(loop, PsiMember.class, PsiLambdaExpression.class);
|
||||
return ReferencesSearch.search(variable)
|
||||
.forEach(reference -> PsiTreeUtil.getParentOfType(reference.getElement(), PsiMember.class, PsiLambdaExpression.class) == loopBound);
|
||||
private static boolean canBeMadeNonFinal(@NotNull PsiLocalVariable variable, @NotNull PsiStatement sourceStatement) {
|
||||
NavigatablePsiElement loopBound = PsiTreeUtil.getParentOfType(sourceStatement, PsiMember.class, PsiLambdaExpression.class);
|
||||
|
||||
Predicate<PsiReference> referenceBoundPredicate;
|
||||
if (sourceStatement instanceof PsiLoopStatement) {
|
||||
referenceBoundPredicate =
|
||||
(reference) -> PsiTreeUtil.getParentOfType(reference.getElement(), PsiMember.class, PsiLambdaExpression.class) == loopBound;
|
||||
}
|
||||
else {
|
||||
referenceBoundPredicate = (reference) -> {
|
||||
PsiLambdaExpression lambda = PsiTreeUtil.getParentOfType(reference.getElement(), PsiLambdaExpression.class);
|
||||
return PsiTreeUtil.getParentOfType(lambda, PsiMember.class, PsiLambdaExpression.class) == loopBound;
|
||||
};
|
||||
}
|
||||
return ReferencesSearch.search(variable).forEach((Processor<PsiReference>)reference -> referenceBoundPredicate.test(reference)) &&
|
||||
FinalUtils.canBeFinal(variable);
|
||||
}
|
||||
|
||||
String generateTerminal() {
|
||||
@@ -197,6 +211,11 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
String generateIntermediate() {
|
||||
if (TypeUtils.isJavaLangString(myLoopVariable.getType()) &&
|
||||
myMainJoinParts.size() == 1 &&
|
||||
myMainJoinParts.get(0) instanceof PsiReferenceExpression) {
|
||||
return "";
|
||||
}
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(myLoopVariable.getProject());
|
||||
String joinTransformation = getExpressionText(myMainJoinParts);
|
||||
PsiExpression mapping = elementFactory.createExpressionFromText(joinTransformation, myLoopVariable);
|
||||
@@ -664,12 +683,13 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
if(declarations == null) return null;
|
||||
PsiMethodCallExpression beforeLoopAppend = getCallBeforeStatement(firstAppendSuccessor, targetBuilder, APPEND, declarations);
|
||||
|
||||
if(!canBeMadeNonFinal(targetBuilder, terminalBlock.getMainLoop())) return null;
|
||||
|
||||
if (!canBeMadeNonFinal(targetBuilder, terminalBlock.getStreamSourceStatement())) return null;
|
||||
|
||||
List<PsiElement> refs = StreamEx.of(ReferencesSearch.search(targetBuilder).findAll())
|
||||
.map(PsiReference::getElement)
|
||||
.remove(e -> PsiTreeUtil.isAncestor(targetBuilder, e, false) ||
|
||||
PsiTreeUtil.isAncestor(terminalBlock.getMainLoop(), e, false))
|
||||
PsiTreeUtil.isAncestor(terminalBlock.getStreamSourceStatement(), e, false))
|
||||
.toList();
|
||||
|
||||
allowedReferencePlaces.add(afterLoopAppend);
|
||||
@@ -732,7 +752,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
if (mainJoinParts == null || mainJoinParts.isEmpty()) return null;
|
||||
PsiLocalVariable targetBuilder = extractStringBuilder(statements.get(0));
|
||||
if (targetBuilder == null) return null;
|
||||
PsiLoopStatement loop = terminalBlock.getMainLoop();
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
PrefixSuffixContext context =
|
||||
PrefixSuffixContext.extractAndVerifyRefs(loop, loop, targetBuilder, terminalBlock, emptyList(), new HashSet<>(emptyList()));
|
||||
if (context == null) return null;
|
||||
@@ -776,7 +796,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
|
||||
List<PsiExpression> mainJoinParts = extractJoinParts(withoutCondition);
|
||||
if (mainJoinParts == null) return null;
|
||||
PsiLoopStatement loop = terminalBlock.getMainLoop();
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
PrefixSuffixContext context =
|
||||
PrefixSuffixContext.extractAndVerifyRefs(loop, loop, targetBuilder, terminalBlock, emptyList(), new HashSet<>(emptyList()));
|
||||
if (context == null) return null;
|
||||
@@ -846,7 +866,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
|
||||
PsiLocalVariable targetBuilder = extractStringBuilder(firstIterationStatements.get(0));
|
||||
if (targetBuilder == null) return null;
|
||||
PsiLoopStatement loop = terminalBlock.getMainLoop();
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
PrefixSuffixContext context =
|
||||
PrefixSuffixContext.extractAndVerifyRefs(loop, loop, targetBuilder, terminalBlock, singletonList(boolVar),
|
||||
new HashSet<>(emptyList()));
|
||||
@@ -896,7 +916,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
List<PsiExpression> mainJoinParts = joinData.getMainJoinParts();
|
||||
List<PsiExpression> delimiterJoinParts = joinData.getDelimiterJoinParts();
|
||||
|
||||
PsiLoopStatement loop = terminalBlock.getMainLoop();
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
|
||||
PsiIfStatement ifStatement = tryCast(PsiTreeUtil.skipWhitespacesAndCommentsForward(loop), PsiIfStatement.class);
|
||||
if(ifStatement == null) return null;
|
||||
@@ -1019,7 +1039,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
PsiLocalVariable targetBuilder = extractStringBuilder(mainStatements.get(0));
|
||||
if (targetBuilder == null) return null;
|
||||
|
||||
PsiLoopStatement loop = terminalBlock.getMainLoop();
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
PrefixSuffixContext context =
|
||||
PrefixSuffixContext.extractAndVerifyRefs(loop, loop, targetBuilder, terminalBlock, singletonList(delimiterVar),
|
||||
new HashSet<>(emptyList()));
|
||||
@@ -1103,7 +1123,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
|
||||
PsiLocalVariable targetBuilder = extractStringBuilder(firstIterationStatements.get(0));
|
||||
if (targetBuilder == null) return null;
|
||||
PsiLoopStatement loop = terminalBlock.getMainLoop();
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
PrefixSuffixContext context =
|
||||
PrefixSuffixContext.extractAndVerifyRefs(loop, loop, targetBuilder, terminalBlock, emptyList(), new HashSet<>(emptyList()));
|
||||
if (context == null) return null;
|
||||
@@ -1180,7 +1200,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
JoinData joinData = JoinData.extractLeftDelimiter(joinParts);
|
||||
List<PsiExpression> delimiterJoinParts = joinData.getDelimiterJoinParts();
|
||||
if (delimiterJoinParts.isEmpty()) return null;
|
||||
PsiLoopStatement loop = terminalBlock.getMainLoop();
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
PsiLocalVariable variable = tryCast(terminalBlock.getVariable(), PsiLocalVariable.class);
|
||||
if (variable == null) return null;
|
||||
PsiLocalVariable targetBuilder = extractStringBuilder(statements.get(0));
|
||||
|
||||
+15
-14
@@ -36,29 +36,29 @@ class MatchMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
@Override
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) {
|
||||
PsiLoopStatement loopStatement = tb.getMainLoop();
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
|
||||
PsiStatement sourceStatement = tb.getStreamSourceStatement();
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
|
||||
if(tb.getSingleStatement() instanceof PsiReturnStatement) {
|
||||
PsiReturnStatement returnStatement = (PsiReturnStatement)tb.getSingleStatement();
|
||||
PsiExpression value = returnStatement.getReturnValue();
|
||||
if (ExpressionUtils.isLiteral(value, Boolean.TRUE) || ExpressionUtils.isLiteral(value, Boolean.FALSE)) {
|
||||
boolean foundResult = (boolean)((PsiLiteralExpression)value).getValue();
|
||||
PsiReturnStatement nextReturnStatement = StreamApiMigrationInspection.getNextReturnStatement(loopStatement);
|
||||
PsiReturnStatement nextReturnStatement = StreamApiMigrationInspection.getNextReturnStatement(sourceStatement);
|
||||
if (nextReturnStatement != null) {
|
||||
PsiExpression returnValue = nextReturnStatement.getReturnValue();
|
||||
if(returnValue == null) return null;
|
||||
String methodName = foundResult ? "anyMatch" : "noneMatch";
|
||||
String streamText = addTerminalOperation(methodName, loopStatement, tb);
|
||||
restoreComments(loopStatement, body);
|
||||
if (nextReturnStatement.getParent() == loopStatement.getParent()) {
|
||||
String streamText = addTerminalOperation(methodName, sourceStatement, tb);
|
||||
restoreComments(sourceStatement, body);
|
||||
if (nextReturnStatement.getParent() == sourceStatement.getParent()) {
|
||||
if(!ExpressionUtils.isLiteral(returnValue, !foundResult)) {
|
||||
streamText+= (foundResult ? "||" : "&&") + ParenthesesUtils.getText(returnValue, ParenthesesUtils.AND_PRECEDENCE);
|
||||
}
|
||||
removeLoop(loopStatement);
|
||||
removeLoop(sourceStatement);
|
||||
return returnValue.replace(elementFactory.createExpressionFromText(streamText, nextReturnStatement));
|
||||
}
|
||||
PsiElement result = loopStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", loopStatement));
|
||||
PsiElement result = sourceStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", sourceStatement));
|
||||
if(!isReachable(nextReturnStatement)) {
|
||||
nextReturnStatement.delete();
|
||||
}
|
||||
@@ -67,11 +67,12 @@ class MatchMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
}
|
||||
PsiStatement[] statements = tb.getStatements();
|
||||
if (!(statements.length == 1 || (statements.length == 2 && ControlFlowUtils.statementBreaksLoop(statements[1], loopStatement)))) {
|
||||
if (!(statements.length == 1 || (sourceStatement instanceof PsiLoopStatement && statements.length == 2 && ControlFlowUtils.statementBreaksLoop(statements[1],
|
||||
(PsiLoopStatement)sourceStatement)))) {
|
||||
return null;
|
||||
}
|
||||
restoreComments(loopStatement, body);
|
||||
String streamText = addTerminalOperation("anyMatch", loopStatement, tb);
|
||||
restoreComments(sourceStatement, body);
|
||||
String streamText = addTerminalOperation("anyMatch", sourceStatement, tb);
|
||||
PsiStatement statement = statements[0];
|
||||
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(statement);
|
||||
if(assignment != null) {
|
||||
@@ -85,7 +86,7 @@ class MatchMigration extends BaseStreamApiMigration {
|
||||
// for(....) if(...) {flag = true; break;}
|
||||
PsiVariable var = (PsiVariable)maybeVar;
|
||||
PsiExpression initializer = var.getInitializer();
|
||||
InitializerUsageStatus status = ControlFlowUtils.getInitializerUsageStatus(var, loopStatement);
|
||||
InitializerUsageStatus status = ControlFlowUtils.getInitializerUsageStatus(var, sourceStatement);
|
||||
if (initializer != null && status != ControlFlowUtils.InitializerUsageStatus.UNKNOWN) {
|
||||
String replacement;
|
||||
if (ExpressionUtils.isLiteral(initializer, Boolean.FALSE) &&
|
||||
@@ -99,13 +100,13 @@ class MatchMigration extends BaseStreamApiMigration {
|
||||
else {
|
||||
replacement = streamText + "?" + rValue.getText() + ":" + initializer.getText();
|
||||
}
|
||||
return replaceInitializer(loopStatement, var, initializer, replacement, status);
|
||||
return replaceInitializer(sourceStatement, var, initializer, replacement, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
String replacement = "if(" + streamText + "){" + statement.getText() + "}";
|
||||
return loopStatement.replace(elementFactory.createStatementFromText(replacement, loopStatement));
|
||||
return sourceStatement.replace(elementFactory.createStatementFromText(replacement, sourceStatement));
|
||||
}
|
||||
|
||||
private static String addTerminalOperation(String methodName, @NotNull PsiElement contextElement, @NotNull TerminalBlock tb) {
|
||||
|
||||
+31
-10
@@ -21,30 +21,33 @@ import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.codeInspection.SimplifyStreamApiCallChainsInspection;
|
||||
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.StreamSource;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiLoopStatement;
|
||||
import com.intellij.psi.PsiStatement;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.impl.PsiDiamondTypeUtil;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
class MigrateToStreamFix implements LocalQuickFix {
|
||||
private BaseStreamApiMigration myMigration;
|
||||
@Nullable private final String myCustomName;
|
||||
|
||||
protected MigrateToStreamFix(BaseStreamApiMigration migration) {
|
||||
protected MigrateToStreamFix(BaseStreamApiMigration migration, @Nullable String customName) {
|
||||
myMigration = migration;
|
||||
myCustomName = customName;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Replace with "+myMigration.getReplacement();
|
||||
return myCustomName!= null? myCustomName: "Replace with "+myMigration.getReplacement();
|
||||
}
|
||||
|
||||
@SuppressWarnings("DialogTitleCapitalization")
|
||||
@@ -63,11 +66,29 @@ class MigrateToStreamFix implements LocalQuickFix {
|
||||
PsiStatement body = loopStatement.getBody();
|
||||
if(body == null || source == null) return;
|
||||
TerminalBlock tb = TerminalBlock.from(source, body);
|
||||
PsiElement result = myMigration.migrate(project, body, tb);
|
||||
if(result != null) {
|
||||
tb.operations().forEach(StreamApiMigrationInspection.Operation::cleanUp);
|
||||
simplifyAndFormat(project, result);
|
||||
}
|
||||
migrate(project, body, tb);
|
||||
} else if(element instanceof PsiExpressionStatement) {
|
||||
PsiMethodCallExpression call = tryCast(((PsiExpressionStatement)element).getExpression(), PsiMethodCallExpression.class);
|
||||
if(call == null) return;
|
||||
|
||||
PsiLambdaExpression lambda = SimplifyForEachInspection.extractLambdaFromForEach(call);
|
||||
if (lambda == null) return;
|
||||
PsiElement lambdaBody = lambda.getBody();
|
||||
SimplifyForEachInspection.ExistingStreamSource
|
||||
source = SimplifyForEachInspection.ExistingStreamSource.extractSource(call, lambda);
|
||||
if(source == null) return;
|
||||
TerminalBlock terminalBlock = SimplifyForEachInspection.extractTerminalBlock(lambdaBody, source);
|
||||
if (terminalBlock == null) return;
|
||||
|
||||
migrate(project, lambdaBody, terminalBlock);
|
||||
}
|
||||
}
|
||||
|
||||
private void migrate(@NotNull Project project, PsiElement block, TerminalBlock tb) {
|
||||
PsiElement result = myMigration.migrate(project, block, tb);
|
||||
if(result != null) {
|
||||
tb.operations().forEach(StreamApiMigrationInspection.Operation::cleanUp);
|
||||
simplifyAndFormat(project, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -40,7 +40,7 @@ public class OperationReductionMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
@Override
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) {
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
|
||||
PsiAssignmentExpression assignment = tb.getSingleExpression(PsiAssignmentExpression.class);
|
||||
if (assignment == null) return null;
|
||||
PsiVariable var = StreamApiMigrationInspection.extractAccumulator(assignment, myReductionOperation.getCompoundAssignmentOp());
|
||||
@@ -71,7 +71,7 @@ public class OperationReductionMigration extends BaseStreamApiMigration {
|
||||
+ String.format(Locale.ENGLISH, ".reduce(%s, (%s, %s) -> %s %s %s)",
|
||||
identity, leftOperand, rightOperand, leftOperand,
|
||||
myReductionOperation.getOperation(), rightOperand);
|
||||
return replaceWithOperation(tb.getMainLoop(), var, stream, type, myReductionOperation);
|
||||
return replaceWithOperation(tb.getStreamSourceStatement(), var, stream, type, myReductionOperation);
|
||||
}
|
||||
|
||||
static class ReductionOperation {
|
||||
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.streamMigration;
|
||||
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.roots.FileIndexFacade;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.light.LightElement;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public class SimplifyForEachInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
private static final CallMatcher.Simple ITERABLE_FOREACH =
|
||||
CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_ITERABLE, "forEach").parameterCount(1);
|
||||
private static final CallMatcher.Simple STREAM_FOREACH_ORDERED =
|
||||
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM, "forEachOrdered").parameterCount(1);
|
||||
private static final CallMatcher STREAM_FOREACH =
|
||||
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM, "forEach", "forEachOrdered").parameterCount(1);
|
||||
private static final CallMatcher FOREACH = CallMatcher.anyOf(
|
||||
STREAM_FOREACH,
|
||||
ITERABLE_FOREACH
|
||||
);
|
||||
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getGroupDisplayName() {
|
||||
return GroupNames.LANGUAGE_LEVEL_SPECIFIC_GROUP_NAME;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "forEach call can be simplified";
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return "SimplifyForEach";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
PsiFile file = holder.getFile();
|
||||
VirtualFile virtualFile = file.getVirtualFile();
|
||||
if (!PsiUtil.isLanguageLevel8OrHigher(file) || virtualFile == null ||
|
||||
!FileIndexFacade.getInstance(holder.getProject()).isInSourceContent(virtualFile)) {
|
||||
return PsiElementVisitor.EMPTY_VISITOR;
|
||||
}
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression call) {
|
||||
PsiLambdaExpression lambda = extractLambdaFromForEach(call);
|
||||
if (lambda == null) return;
|
||||
PsiElement lambdaBody = lambda.getBody();
|
||||
ExistingStreamSource
|
||||
source = ExistingStreamSource.extractSource(call, lambda);
|
||||
if (source == null) return;
|
||||
TerminalBlock terminalBlock = extractTerminalBlock(lambdaBody, source);
|
||||
if (terminalBlock == null) return;
|
||||
|
||||
PsiStatement mainStatement = source.getMainStatement();
|
||||
BaseStreamApiMigration migration =
|
||||
StreamApiMigrationInspection.findMigration(mainStatement, lambdaBody, terminalBlock, holder, true, true);
|
||||
boolean opCountChanged = terminalBlock.getOperationCount() > 1;
|
||||
boolean lastOpChanged = !(migration instanceof ForEachMigration);
|
||||
if (opCountChanged || lastOpChanged) {
|
||||
String customMessage;
|
||||
if (opCountChanged && !lastOpChanged) {
|
||||
customMessage = "Extract intermediate operations";
|
||||
if (STREAM_FOREACH_ORDERED.test(call)) {
|
||||
migration = new ForEachMigration(migration.isShouldWarn(), "forEachOrdered");
|
||||
}
|
||||
}
|
||||
else {
|
||||
customMessage = null;
|
||||
}
|
||||
if (migration != null) {
|
||||
migration.setShouldWarn(true);
|
||||
}
|
||||
|
||||
StreamApiMigrationInspection
|
||||
.offerMigration(mainStatement, terminalBlock, migration, m -> getRange(call).shiftRight(-call.getTextOffset()), customMessage, false, holder);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static TextRange getRange(PsiMethodCallExpression call) {
|
||||
PsiReferenceExpression methodExpression = call.getMethodExpression();
|
||||
return new TextRange(methodExpression.getTextOffset(), call.getArgumentList().getTextOffset());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static TerminalBlock extractTerminalBlock(@Nullable PsiElement lambdaBody,
|
||||
@NotNull ExistingStreamSource source) {
|
||||
if (lambdaBody instanceof PsiCodeBlock) {
|
||||
return TerminalBlock.from(source, (PsiCodeBlock)lambdaBody);
|
||||
}
|
||||
if (lambdaBody instanceof PsiExpression) {
|
||||
return TerminalBlock.fromStatements(source, new LightExpressionStatement((PsiExpression)lambdaBody));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static PsiLambdaExpression extractLambdaFromForEach(PsiMethodCallExpression call) {
|
||||
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null ||
|
||||
!FOREACH.test(call) ||
|
||||
!InheritanceUtil.isInheritor(qualifier.getType(), CommonClassNames.JAVA_UTIL_COLLECTION)) return null;
|
||||
PsiExpression arg = call.getArgumentList().getExpressions()[0];
|
||||
return tryCast(PsiUtil.skipParenthesizedExprDown(arg), PsiLambdaExpression.class);
|
||||
}
|
||||
|
||||
static class LightExpressionStatement extends LightElement implements PsiExpressionStatement {
|
||||
@NotNull private final PsiExpression myExpression;
|
||||
|
||||
protected LightExpressionStatement(@NotNull PsiExpression expression) {
|
||||
super(expression.getManager(), JavaLanguage.INSTANCE);
|
||||
myExpression = expression;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiExpression getExpression() {
|
||||
return myExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myExpression.getText() + ";";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class ExistingStreamSource extends StreamApiMigrationInspection.StreamSource {
|
||||
private final boolean myIsCollectionForEach;
|
||||
|
||||
protected ExistingStreamSource(PsiStatement mainStatement, PsiVariable variable, PsiExpression expression, boolean isCollectionForEach) {
|
||||
super(mainStatement, variable, expression);
|
||||
myIsCollectionForEach = isCollectionForEach;
|
||||
}
|
||||
|
||||
@Override
|
||||
String createReplacement() {
|
||||
return myExpression.getText() + (myIsCollectionForEach? ".stream()" : "");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static ExistingStreamSource extractSource(PsiMethodCallExpression call, PsiLambdaExpression lambda) {
|
||||
PsiParameter[] parameters = lambda.getParameterList().getParameters();
|
||||
if (parameters.length != 1) return null;
|
||||
PsiParameter parameter = parameters[0];
|
||||
|
||||
boolean isCollectionForEach = ITERABLE_FOREACH.test(call);
|
||||
|
||||
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) return null;
|
||||
PsiStatement parent = tryCast(call.getParent(), PsiExpressionStatement.class);
|
||||
if (parent == null) return null;
|
||||
return new ExistingStreamSource(parent, parameter, qualifier, isCollectionForEach);
|
||||
}
|
||||
}
|
||||
}
|
||||
+221
-192
@@ -25,6 +25,7 @@ import com.intellij.codeInspection.LambdaCanBeMethodReferenceInspection;
|
||||
import com.intellij.codeInspection.ProblemHighlightType;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.FileIndexFacade;
|
||||
@@ -33,6 +34,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.controlFlow.*;
|
||||
import com.intellij.psi.impl.light.LightElement;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
@@ -41,6 +43,8 @@ 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.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.*;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
@@ -52,6 +56,7 @@ import javax.swing.*;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static com.intellij.codeInspection.streamMigration.OperationReductionMigration.SUM_OPERATION;
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
@@ -60,8 +65,10 @@ import static com.siyeh.ig.psiutils.ControlFlowUtils.InitializerUsageStatus.UNKN
|
||||
public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
private static final Logger LOG = Logger.getInstance(StreamApiMigrationInspection.class);
|
||||
|
||||
|
||||
public boolean REPLACE_TRIVIAL_FOREACH;
|
||||
public boolean SUGGEST_FOREACH;
|
||||
private static final String SHORT_NAME = "Convert2streamapi";
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@@ -94,7 +101,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
@NotNull
|
||||
@Override
|
||||
public String getShortName() {
|
||||
return "Convert2streamapi";
|
||||
return SHORT_NAME;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -315,7 +322,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
}
|
||||
return variable != null &&
|
||||
ExpressionUtils.isZero(variable.getInitializer()) &&
|
||||
ControlFlowUtils.getInitializerUsageStatus(variable, tb.getMainLoop()) != UNKNOWN;
|
||||
ControlFlowUtils.getInitializerUsageStatus(variable, tb.getStreamSourceStatement()) != UNKNOWN;
|
||||
}
|
||||
|
||||
private static boolean isTrivial(TerminalBlock tb) {
|
||||
@@ -437,203 +444,222 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
if (!ExceptionUtil.getThrownCheckedExceptions(body).isEmpty()) return;
|
||||
TerminalBlock tb = TerminalBlock.from(source, body);
|
||||
|
||||
BaseStreamApiMigration migration = findMigration(statement, body, tb);
|
||||
if (migration != null && (myIsOnTheFly || migration.isShouldWarn())) {
|
||||
MigrateToStreamFix[] fixes = {new MigrateToStreamFix(migration)};
|
||||
if (migration instanceof ForEachMigration && !(tb.getLastOperation() instanceof CollectionStream)) { //for .stream()
|
||||
fixes = ArrayUtil.append(fixes, new MigrateToStreamFix(new ForEachMigration(migration.isShouldWarn(), "forEachOrdered")));
|
||||
}
|
||||
ProblemHighlightType highlightType =
|
||||
migration.isShouldWarn() ? ProblemHighlightType.GENERIC_ERROR_OR_WARNING : ProblemHighlightType.INFORMATION;
|
||||
myHolder.registerProblem(statement, "Can be replaced with '" + migration.getReplacement() + "' call",
|
||||
highlightType, getRange(migration.isShouldWarn(), statement).shiftRight(-statement.getTextOffset()),
|
||||
fixes);
|
||||
}
|
||||
BaseStreamApiMigration migration = findMigration(statement, body, tb, myHolder, SUGGEST_FOREACH, REPLACE_TRIVIAL_FOREACH);
|
||||
offerMigration(statement, tb, migration, (streamApiMigration) -> getRange(streamApiMigration.isShouldWarn(), statement, myIsOnTheFly)
|
||||
.shiftRight(-statement.getTextOffset()), null, myIsOnTheFly, myHolder);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BaseStreamApiMigration findMigration(PsiLoopStatement loop, PsiStatement body, TerminalBlock tb) {
|
||||
final ControlFlow controlFlow;
|
||||
try {
|
||||
controlFlow = ControlFlowFactory.getInstance(myHolder.getProject())
|
||||
.getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance());
|
||||
}
|
||||
catch (AnalysisCanceledException ignored) {
|
||||
return null;
|
||||
}
|
||||
int startOffset = controlFlow.getStartOffset(body);
|
||||
int endOffset = controlFlow.getEndOffset(body);
|
||||
if (startOffset < 0 || endOffset < 0) return null;
|
||||
PsiElement surrounder = PsiTreeUtil.getParentOfType(loop, PsiLambdaExpression.class, PsiClass.class);
|
||||
final List<PsiVariable> nonFinalVariables = StreamEx.of(ControlFlowUtil.getUsedVariables(controlFlow, startOffset, endOffset))
|
||||
.remove(variable -> variable instanceof PsiField)
|
||||
.remove(variable -> PsiTreeUtil.getParentOfType(variable, PsiLambdaExpression.class, PsiClass.class) != surrounder)
|
||||
.remove(variable -> isVariableSuitableForStream(variable, loop, tb)).toList();
|
||||
static void offerMigration(PsiStatement statement,
|
||||
TerminalBlock tb,
|
||||
BaseStreamApiMigration migration,
|
||||
Function<BaseStreamApiMigration, TextRange> rangeSupplier,
|
||||
@Nullable String customMessage,
|
||||
boolean isOnTheFly,
|
||||
ProblemsHolder holder) {
|
||||
if (migration != null && (isOnTheFly || migration.isShouldWarn())) {
|
||||
|
||||
if (isCountOperation(nonFinalVariables, tb)) {
|
||||
return new CountMigration(true);
|
||||
}
|
||||
if (nonFinalVariables.size() == 0) {
|
||||
CollectMigration.CollectTerminal terminal = CollectMigration.extractCollectTerminal(tb, nonFinalVariables);
|
||||
if (terminal != null) {
|
||||
boolean addAll = loop 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;
|
||||
boolean shouldWarn = REPLACE_TRIVIAL_FOREACH ||
|
||||
tb.hasOperations() ||
|
||||
tb.getLastOperation() instanceof BufferedReaderLines ||
|
||||
!terminal.isTrivial();
|
||||
return new CollectMigration(shouldWarn, terminal.getMethodName());
|
||||
}
|
||||
}
|
||||
if(JoiningMigration.extractTerminal(tb, nonFinalVariables) != null) {
|
||||
return new JoiningMigration(true);
|
||||
}
|
||||
if (tb.getCountExpression() != null || tb.isEmpty()) return null;
|
||||
if (nonFinalVariables.isEmpty() && extractArray(tb) != null) {
|
||||
return new ToArrayMigration(true);
|
||||
}
|
||||
if (getAccumulatedVariable(tb, nonFinalVariables, SUM_OPERATION) != null) {
|
||||
return new SumMigration(true);
|
||||
}
|
||||
FindExtremumMigration.ExtremumTerminal extremumTerminal = FindExtremumMigration.extract(tb, nonFinalVariables);
|
||||
if(extremumTerminal != null) {
|
||||
return new FindExtremumMigration(true, FindExtremumMigration.getOperation(extremumTerminal.isMax()) + "()");
|
||||
}
|
||||
for (OperationReductionMigration.ReductionOperation reductionOperation : OperationReductionMigration.OPERATIONS) {
|
||||
if (getAccumulatedVariable(tb, nonFinalVariables, reductionOperation) != null) {
|
||||
return new OperationReductionMigration(true, reductionOperation);
|
||||
}
|
||||
}
|
||||
Collection<PsiStatement> exitPoints = tb.findExitPoints(controlFlow);
|
||||
if (exitPoints == null) return null;
|
||||
boolean onlyNonLabeledContinue = StreamEx.of(exitPoints).allMatch(statement -> statement instanceof PsiContinueStatement &&
|
||||
((PsiContinueStatement)statement).getLabelIdentifier() == null);
|
||||
if (onlyNonLabeledContinue && nonFinalVariables.isEmpty()) {
|
||||
boolean shouldWarn = SUGGEST_FOREACH &&
|
||||
(REPLACE_TRIVIAL_FOREACH ||
|
||||
tb.hasOperations() ||
|
||||
ForEachMigration.tryExtractMapExpression(tb) != null ||
|
||||
!isTrivial(tb));
|
||||
return new ForEachMigration(shouldWarn, "forEach");
|
||||
}
|
||||
if (nonFinalVariables.isEmpty() && tb.getSingleStatement() instanceof PsiReturnStatement) {
|
||||
return findMigrationForReturn(loop, tb);
|
||||
}
|
||||
// Source and intermediate ops should not refer to non-final variables
|
||||
if (tb.intermediateAndSourceExpressions()
|
||||
.flatCollection(expr -> PsiTreeUtil.collectElementsOfType(expr, PsiReferenceExpression.class))
|
||||
.map(PsiReferenceExpression::resolve).select(PsiVariable.class).anyMatch(nonFinalVariables::contains)) {
|
||||
return null;
|
||||
}
|
||||
PsiStatement[] statements = tb.getStatements();
|
||||
if (statements.length == 2) {
|
||||
PsiStatement breakStatement = statements[1];
|
||||
if (ControlFlowUtils.statementBreaksLoop(breakStatement, loop) &&
|
||||
exitPoints.size() == 1 &&
|
||||
exitPoints.contains(breakStatement)) {
|
||||
return findMigrationForBreak(tb, nonFinalVariables, statements[0]);
|
||||
}
|
||||
MigrateToStreamFix[] fixes = {new MigrateToStreamFix(migration, customMessage)};
|
||||
if (migration instanceof ForEachMigration && !(tb.getLastOperation() instanceof CollectionStream)) { //for .stream()
|
||||
fixes = ArrayUtil.append(fixes, new MigrateToStreamFix(new ForEachMigration(migration.isShouldWarn(), "forEachOrdered"), customMessage));
|
||||
}
|
||||
ProblemHighlightType highlightType =
|
||||
migration.isShouldWarn() ? ProblemHighlightType.GENERIC_ERROR_OR_WARNING : ProblemHighlightType.INFORMATION;
|
||||
String message = customMessage != null ? customMessage : "Can be replaced with '" + migration.getReplacement() + "' call";
|
||||
holder.registerProblem(statement, message, highlightType, rangeSupplier.apply(migration), fixes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
static BaseStreamApiMigration findMigration(PsiStatement loop,
|
||||
PsiElement body,
|
||||
TerminalBlock tb,
|
||||
ProblemsHolder holder,
|
||||
boolean suggestForeach,
|
||||
boolean replaceTrivialForEach) {
|
||||
final ControlFlow controlFlow;
|
||||
try {
|
||||
controlFlow = ControlFlowFactory.getInstance(holder.getProject())
|
||||
.getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance());
|
||||
}
|
||||
catch (AnalysisCanceledException ignored) {
|
||||
return null;
|
||||
}
|
||||
int startOffset = controlFlow.getStartOffset(body);
|
||||
int endOffset = controlFlow.getEndOffset(body);
|
||||
if (startOffset < 0 || endOffset < 0) return null;
|
||||
PsiElement surrounder = PsiTreeUtil.getParentOfType(loop, PsiLambdaExpression.class, PsiClass.class);
|
||||
final List<PsiVariable> nonFinalVariables = StreamEx.of(ControlFlowUtil.getUsedVariables(controlFlow, startOffset, endOffset))
|
||||
.remove(variable -> variable instanceof PsiField)
|
||||
.remove(variable -> PsiTreeUtil.getParentOfType(variable, PsiLambdaExpression.class, PsiClass.class) != surrounder)
|
||||
.remove(variable -> isVariableSuitableForStream(variable, loop, tb)).toList();
|
||||
|
||||
@Nullable
|
||||
private BaseStreamApiMigration findMigrationForBreak(TerminalBlock tb, List<PsiVariable> nonFinalVariables, PsiStatement statement) {
|
||||
boolean shouldWarn = REPLACE_TRIVIAL_FOREACH || tb.hasOperations();
|
||||
if (ReferencesSearch.search(tb.getVariable(), new LocalSearchScope(statement)).findFirst() == null) {
|
||||
return new MatchMigration(shouldWarn, "anyMatch");
|
||||
if (isCountOperation(nonFinalVariables, tb)) {
|
||||
return new CountMigration(true);
|
||||
}
|
||||
if (nonFinalVariables.size() == 0) {
|
||||
CollectMigration.CollectTerminal terminal = CollectMigration.extractCollectTerminal(tb, nonFinalVariables);
|
||||
if (terminal != null) {
|
||||
boolean addAll = loop 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;
|
||||
boolean shouldWarn = replaceTrivialForEach ||
|
||||
tb.hasOperations() ||
|
||||
tb.getLastOperation() instanceof BufferedReaderLines ||
|
||||
!terminal.isTrivial();
|
||||
return new CollectMigration(shouldWarn, terminal.getMethodName());
|
||||
}
|
||||
if (nonFinalVariables.isEmpty() && statement instanceof PsiExpressionStatement) {
|
||||
return new FindFirstMigration(shouldWarn);
|
||||
}
|
||||
if (nonFinalVariables.size() == 1) {
|
||||
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(statement);
|
||||
if (assignment == null) return null;
|
||||
PsiReferenceExpression lValue = tryCast(assignment.getLExpression(), PsiReferenceExpression.class);
|
||||
if (lValue == null) return null;
|
||||
PsiVariable var = tryCast(lValue.resolve(), PsiVariable.class);
|
||||
if (var == null || !nonFinalVariables.contains(var)) return null;
|
||||
PsiExpression rValue = assignment.getRExpression();
|
||||
if (rValue == null || VariableAccessUtils.variableIsUsed(var, rValue)) return null;
|
||||
if (tb.getVariable().getType() instanceof PsiPrimitiveType && !ExpressionUtils.isReferenceTo(rValue, tb.getVariable())) return null;
|
||||
return new FindFirstMigration(shouldWarn);
|
||||
}
|
||||
if(JoiningMigration.extractTerminal(tb, nonFinalVariables) != null) {
|
||||
return new JoiningMigration(true);
|
||||
}
|
||||
if (tb.getCountExpression() != null || tb.isEmpty()) return null;
|
||||
if (nonFinalVariables.isEmpty() && extractArray(tb) != null) {
|
||||
return new ToArrayMigration(true);
|
||||
}
|
||||
if (getAccumulatedVariable(tb, nonFinalVariables, SUM_OPERATION) != null) {
|
||||
return new SumMigration(true);
|
||||
}
|
||||
FindExtremumMigration.ExtremumTerminal extremumTerminal = FindExtremumMigration.extract(tb, nonFinalVariables);
|
||||
if(extremumTerminal != null) {
|
||||
return new FindExtremumMigration(true, FindExtremumMigration.getOperation(extremumTerminal.isMax()) + "()");
|
||||
}
|
||||
for (OperationReductionMigration.ReductionOperation reductionOperation : OperationReductionMigration.OPERATIONS) {
|
||||
if (getAccumulatedVariable(tb, nonFinalVariables, reductionOperation) != null) {
|
||||
return new OperationReductionMigration(true, reductionOperation);
|
||||
}
|
||||
}
|
||||
Collection<PsiStatement> exitPoints = tb.findExitPoints(controlFlow);
|
||||
if (exitPoints == null) return null;
|
||||
boolean onlyNonLabeledContinue = StreamEx.of(exitPoints).allMatch(statement -> statement instanceof PsiContinueStatement && ((PsiContinueStatement)statement).getLabelIdentifier() == null);
|
||||
if (onlyNonLabeledContinue && nonFinalVariables.isEmpty()) {
|
||||
boolean shouldWarn = suggestForeach &&
|
||||
(replaceTrivialForEach ||
|
||||
tb.hasOperations() ||
|
||||
ForEachMigration.tryExtractMapExpression(tb) != null ||
|
||||
!isTrivial(tb));
|
||||
return new ForEachMigration(shouldWarn, "forEach");
|
||||
}
|
||||
if (nonFinalVariables.isEmpty() && tb.getSingleStatement() instanceof PsiReturnStatement) {
|
||||
return findMigrationForReturn(loop, tb, replaceTrivialForEach);
|
||||
}
|
||||
// Source and intermediate ops should not refer to non-final variables
|
||||
if (tb.intermediateAndSourceExpressions()
|
||||
.flatCollection(expr -> PsiTreeUtil.collectElementsOfType(expr, PsiReferenceExpression.class))
|
||||
.map(PsiReferenceExpression::resolve).select(PsiVariable.class).anyMatch(nonFinalVariables::contains)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BaseStreamApiMigration findMigrationForReturn(PsiLoopStatement statement, TerminalBlock tb) {
|
||||
boolean shouldWarn = REPLACE_TRIVIAL_FOREACH || tb.hasOperations();
|
||||
PsiReturnStatement returnStatement = (PsiReturnStatement)tb.getSingleStatement();
|
||||
PsiExpression value = returnStatement.getReturnValue();
|
||||
PsiReturnStatement nextReturnStatement = getNextReturnStatement(statement);
|
||||
if (nextReturnStatement != null &&
|
||||
(ExpressionUtils.isLiteral(value, Boolean.TRUE) || ExpressionUtils.isLiteral(value, Boolean.FALSE))) {
|
||||
boolean foundResult = (boolean)((PsiLiteralExpression)value).getValue();
|
||||
String methodName;
|
||||
if (foundResult) {
|
||||
methodName = "anyMatch";
|
||||
}
|
||||
else {
|
||||
methodName = "noneMatch";
|
||||
FilterOp lastFilter = tb.getLastOperation(FilterOp.class);
|
||||
if (lastFilter != null && (lastFilter.isNegated() ^ BoolUtils.isNegation(lastFilter.getExpression()))) {
|
||||
methodName = "allMatch";
|
||||
}
|
||||
}
|
||||
if (nextReturnStatement.getParent() == statement.getParent() ||
|
||||
ExpressionUtils.isLiteral(nextReturnStatement.getReturnValue(), !foundResult)) {
|
||||
return new MatchMigration(shouldWarn, methodName);
|
||||
}
|
||||
PsiStatement[] statements = tb.getStatements();
|
||||
if (statements.length == 2) {
|
||||
PsiStatement breakStatement = statements[1];
|
||||
if (loop instanceof PsiLoopStatement && ControlFlowUtils.statementBreaksLoop(breakStatement, (PsiLoopStatement)loop) &&
|
||||
exitPoints.size() == 1 &&
|
||||
exitPoints.contains(breakStatement)) {
|
||||
return findMigrationForBreak(tb, nonFinalVariables, statements[0], replaceTrivialForEach);
|
||||
}
|
||||
if (!VariableAccessUtils.variableIsUsed(tb.getVariable(), value)) {
|
||||
if (!REPLACE_TRIVIAL_FOREACH && !tb.hasOperations() ||
|
||||
(tb.getLastOperation() instanceof FilterOp && tb.operations().count() == 2)) {
|
||||
return null;
|
||||
}
|
||||
return new MatchMigration(shouldWarn, "anyMatch");
|
||||
}
|
||||
if (nextReturnStatement != null && ExpressionUtils.isSimpleExpression(nextReturnStatement.getReturnValue())
|
||||
&& (!(tb.getVariable().getType() instanceof PsiPrimitiveType) || ExpressionUtils.isReferenceTo(value, tb.getVariable()))) {
|
||||
return new FindFirstMigration(shouldWarn);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TextRange getRange(boolean shouldWarn, PsiLoopStatement statement) {
|
||||
boolean wholeStatement =
|
||||
myIsOnTheFly && (!shouldWarn || InspectionProjectProfileManager.isInformationLevel(getShortName(), statement));
|
||||
if (statement instanceof PsiForeachStatement) {
|
||||
PsiJavaToken rParenth = ((PsiForeachStatement)statement).getRParenth();
|
||||
if (wholeStatement && rParenth != null) {
|
||||
return new TextRange(statement.getTextOffset(), rParenth.getTextOffset() + 1);
|
||||
}
|
||||
PsiExpression iteratedValue = ((PsiForeachStatement)statement).getIteratedValue();
|
||||
LOG.assertTrue(iteratedValue != null);
|
||||
return iteratedValue.getTextRange();
|
||||
}
|
||||
else if (statement instanceof PsiForStatement) {
|
||||
PsiJavaToken rParenth = ((PsiForStatement)statement).getRParenth();
|
||||
if (wholeStatement && rParenth != null) {
|
||||
return new TextRange(statement.getTextOffset(), rParenth.getTextOffset() + 1);
|
||||
}
|
||||
PsiStatement initialization = ((PsiForStatement)statement).getInitialization();
|
||||
LOG.assertTrue(initialization != null);
|
||||
return initialization.getTextRange();
|
||||
}
|
||||
else if (statement instanceof PsiWhileStatement) {
|
||||
PsiJavaToken rParenth = ((PsiWhileStatement)statement).getRParenth();
|
||||
if (wholeStatement && rParenth != null) {
|
||||
return new TextRange(statement.getTextOffset(), rParenth.getTextOffset() + 1);
|
||||
}
|
||||
return statement.getFirstChild().getTextRange();
|
||||
@Nullable
|
||||
private static BaseStreamApiMigration findMigrationForBreak(TerminalBlock tb,
|
||||
List<PsiVariable> nonFinalVariables,
|
||||
PsiStatement statement,
|
||||
boolean replaceTrivialForEach) {
|
||||
boolean shouldWarn = replaceTrivialForEach || tb.hasOperations();
|
||||
if (ReferencesSearch.search(tb.getVariable(), new LocalSearchScope(statement)).findFirst() == null) {
|
||||
return new MatchMigration(shouldWarn, "anyMatch");
|
||||
}
|
||||
if (nonFinalVariables.isEmpty() && statement instanceof PsiExpressionStatement) {
|
||||
return new FindFirstMigration(shouldWarn);
|
||||
}
|
||||
if (nonFinalVariables.size() == 1) {
|
||||
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(statement);
|
||||
if (assignment == null) return null;
|
||||
PsiReferenceExpression lValue = tryCast(assignment.getLExpression(), PsiReferenceExpression.class);
|
||||
if (lValue == null) return null;
|
||||
PsiVariable var = tryCast(lValue.resolve(), PsiVariable.class);
|
||||
if (var == null || !nonFinalVariables.contains(var)) return null;
|
||||
PsiExpression rValue = assignment.getRExpression();
|
||||
if (rValue == null || VariableAccessUtils.variableIsUsed(var, rValue)) return null;
|
||||
if (tb.getVariable().getType() instanceof PsiPrimitiveType && !ExpressionUtils.isReferenceTo(rValue, tb.getVariable())) return null;
|
||||
return new FindFirstMigration(shouldWarn);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static BaseStreamApiMigration findMigrationForReturn(PsiStatement statement, TerminalBlock tb, boolean replaceTrivialForEach) {
|
||||
boolean shouldWarn = replaceTrivialForEach || tb.hasOperations();
|
||||
PsiReturnStatement returnStatement = (PsiReturnStatement)tb.getSingleStatement();
|
||||
PsiExpression value = returnStatement.getReturnValue();
|
||||
PsiReturnStatement nextReturnStatement = getNextReturnStatement(statement);
|
||||
if (nextReturnStatement != null &&
|
||||
(ExpressionUtils.isLiteral(value, Boolean.TRUE) || ExpressionUtils.isLiteral(value, Boolean.FALSE))) {
|
||||
boolean foundResult = (boolean)((PsiLiteralExpression)value).getValue();
|
||||
String methodName;
|
||||
if (foundResult) {
|
||||
methodName = "anyMatch";
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Unexpected statement type: " + statement);
|
||||
methodName = "noneMatch";
|
||||
FilterOp lastFilter = tb.getLastOperation(FilterOp.class);
|
||||
if (lastFilter != null && (lastFilter.isNegated() ^ BoolUtils.isNegation(lastFilter.getExpression()))) {
|
||||
methodName = "allMatch";
|
||||
}
|
||||
}
|
||||
if (nextReturnStatement.getParent() == statement.getParent() ||
|
||||
ExpressionUtils.isLiteral(nextReturnStatement.getReturnValue(), !foundResult)) {
|
||||
return new MatchMigration(shouldWarn, methodName);
|
||||
}
|
||||
}
|
||||
if (!VariableAccessUtils.variableIsUsed(tb.getVariable(), value)) {
|
||||
if (!replaceTrivialForEach && !tb.hasOperations() ||
|
||||
(tb.getLastOperation() instanceof FilterOp && tb.operations().count() == 2)) {
|
||||
return null;
|
||||
}
|
||||
return new MatchMigration(shouldWarn, "anyMatch");
|
||||
}
|
||||
if (nextReturnStatement != null && ExpressionUtils.isSimpleExpression(nextReturnStatement.getReturnValue())
|
||||
&& (!(tb.getVariable().getType() instanceof PsiPrimitiveType) || ExpressionUtils.isReferenceTo(value, tb.getVariable()))) {
|
||||
return new FindFirstMigration(shouldWarn);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static TextRange getRange(boolean shouldWarn, PsiStatement statement, boolean isOnTheFly) {
|
||||
boolean wholeStatement =
|
||||
isOnTheFly && (!shouldWarn || InspectionProjectProfileManager.isInformationLevel(SHORT_NAME, statement));
|
||||
if (statement instanceof PsiForeachStatement) {
|
||||
PsiJavaToken rParenth = ((PsiForeachStatement)statement).getRParenth();
|
||||
if (wholeStatement && rParenth != null) {
|
||||
return new TextRange(statement.getTextOffset(), rParenth.getTextOffset() + 1);
|
||||
}
|
||||
PsiExpression iteratedValue = ((PsiForeachStatement)statement).getIteratedValue();
|
||||
LOG.assertTrue(iteratedValue != null);
|
||||
return iteratedValue.getTextRange();
|
||||
}
|
||||
else if (statement instanceof PsiForStatement) {
|
||||
PsiJavaToken rParenth = ((PsiForStatement)statement).getRParenth();
|
||||
if (wholeStatement && rParenth != null) {
|
||||
return new TextRange(statement.getTextOffset(), rParenth.getTextOffset() + 1);
|
||||
}
|
||||
PsiStatement initialization = ((PsiForStatement)statement).getInitialization();
|
||||
LOG.assertTrue(initialization != null);
|
||||
return initialization.getTextRange();
|
||||
}
|
||||
else if (statement instanceof PsiWhileStatement) {
|
||||
PsiJavaToken rParenth = ((PsiWhileStatement)statement).getRParenth();
|
||||
if (wholeStatement && rParenth != null) {
|
||||
return new TextRange(statement.getTextOffset(), rParenth.getTextOffset() + 1);
|
||||
}
|
||||
return statement.getFirstChild().getTextRange();
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Unexpected statement type: " + statement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,7 +675,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
PsiReferenceExpression arrayReference = tryCast(arrayAccess.getArrayExpression(), PsiReferenceExpression.class);
|
||||
if (arrayReference == null) return null;
|
||||
PsiLocalVariable arrayVariable = tryCast(arrayReference.resolve(), PsiLocalVariable.class);
|
||||
if (arrayVariable == null || ControlFlowUtils.getInitializerUsageStatus(arrayVariable, tb.getMainLoop()) == UNKNOWN) return null;
|
||||
if (arrayVariable == null || ControlFlowUtils.getInitializerUsageStatus(arrayVariable, tb.getStreamSourceStatement()) == UNKNOWN) {
|
||||
return null;
|
||||
}
|
||||
PsiNewExpression initializer = tryCast(arrayVariable.getInitializer(), PsiNewExpression.class);
|
||||
if (initializer == null) return null;
|
||||
PsiArrayType arrayType = tryCast(initializer.getType(), PsiArrayType.class);
|
||||
@@ -843,7 +871,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
}
|
||||
|
||||
boolean breaksMe(PsiBreakStatement statement) {
|
||||
return statement.findExitedStatement() == mySource.getLoop();
|
||||
return statement.findExitedStatement() == mySource.getMainStatement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -915,15 +943,15 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
}
|
||||
|
||||
abstract static class StreamSource extends Operation {
|
||||
private final PsiLoopStatement myLoop;
|
||||
private final PsiStatement myMainStatement;
|
||||
|
||||
protected StreamSource(PsiLoopStatement loop, PsiVariable variable, PsiExpression expression) {
|
||||
protected StreamSource(PsiStatement mainStatement, PsiVariable variable, PsiExpression expression) {
|
||||
super(expression, variable);
|
||||
myLoop = loop;
|
||||
myMainStatement = mainStatement;
|
||||
}
|
||||
|
||||
PsiLoopStatement getLoop() {
|
||||
return myLoop;
|
||||
PsiStatement getMainStatement() {
|
||||
return myMainStatement;
|
||||
}
|
||||
|
||||
@Contract("null -> null")
|
||||
@@ -1086,7 +1114,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
final PsiExpression myBound;
|
||||
final boolean myIncluding;
|
||||
|
||||
private CountingLoopSource(PsiLoopStatement loop,
|
||||
private CountingLoopSource(PsiStatement loop,
|
||||
PsiVariable counter,
|
||||
PsiExpression initializer,
|
||||
PsiExpression bound,
|
||||
@@ -1109,11 +1137,11 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
}
|
||||
|
||||
CountingLoopSource withBound(PsiExpression bound) {
|
||||
return new CountingLoopSource(getLoop(), getVariable(), getExpression(), bound, myIncluding);
|
||||
return new CountingLoopSource(getMainStatement(), getVariable(), getExpression(), bound, myIncluding);
|
||||
}
|
||||
|
||||
CountingLoopSource withInitializer(PsiExpression expression) {
|
||||
return new CountingLoopSource(getLoop(), getVariable(), expression, myBound, myIncluding);
|
||||
return new CountingLoopSource(getMainStatement(), getVariable(), expression, myBound, myIncluding);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1139,4 +1167,5 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
return new CountingLoopSource(forStatement, loop.getCounter(), loop.getInitializer(), loop.getBound(), loop.isIncluding());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class SumMigration extends BaseStreamApiMigration {
|
||||
SumMigration(boolean shouldWarn) {super(shouldWarn, "sum()");}
|
||||
|
||||
@Override
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) {
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
|
||||
PsiAssignmentExpression assignment = tb.getSingleExpression(PsiAssignmentExpression.class);
|
||||
if (assignment == null) return null;
|
||||
PsiVariable var = StreamApiMigrationInspection.extractSumAccumulator(assignment);
|
||||
@@ -51,6 +51,6 @@ class SumMigration extends BaseStreamApiMigration {
|
||||
"(" + type.getCanonicalText() + ")" + ParenthesesUtils.getText(addend, ParenthesesUtils.MULTIPLICATIVE_PRECEDENCE), addend);
|
||||
}
|
||||
String stream = tb.add(new MapOp(addend, tb.getVariable(), type)).generate()+".sum()";
|
||||
return replaceWithOperation(tb.getMainLoop(), var, stream, type, SUM_OPERATION);
|
||||
return replaceWithOperation(tb.getStreamSourceStatement(), var, stream, type, SUM_OPERATION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,10 @@ class TerminalBlock {
|
||||
return null;
|
||||
}
|
||||
|
||||
int getOperationCount() {
|
||||
return myOperations.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PsiMethodCallExpression if this TerminalBlock contains single method call, null otherwise
|
||||
*/
|
||||
@@ -191,12 +195,14 @@ class TerminalBlock {
|
||||
}
|
||||
if(myStatements.length >= 1) {
|
||||
PsiStatement first = myStatements[0];
|
||||
if(PsiUtil.isLanguageLevel9OrHigher(first.getContainingFile()) && first instanceof PsiIfStatement) {
|
||||
if(PsiUtil.isLanguageLevel9OrHigher(myVariable.getContainingFile()) && first instanceof PsiIfStatement) {
|
||||
PsiIfStatement ifStatement = (PsiIfStatement)first;
|
||||
PsiExpression condition = ifStatement.getCondition();
|
||||
if(ifStatement.getElseBranch() == null && condition != null) {
|
||||
PsiStatement thenStatement = ControlFlowUtils.stripBraces(ifStatement.getThenBranch());
|
||||
if(ControlFlowUtils.statementBreaksLoop(thenStatement, getMainLoop())) {
|
||||
PsiStatement sourceStatement = getStreamSourceStatement();
|
||||
if( sourceStatement instanceof PsiLoopStatement && ControlFlowUtils.statementBreaksLoop(thenStatement,
|
||||
(PsiLoopStatement)sourceStatement)) {
|
||||
TakeWhileOp op = new TakeWhileOp(condition, myVariable, true);
|
||||
PsiStatement[] leftOver = Arrays.copyOfRange(myStatements, 1, myStatements.length);
|
||||
return new TerminalBlock(this, op, myVariable, leftOver);
|
||||
@@ -252,7 +258,9 @@ class TerminalBlock {
|
||||
statements = Arrays.copyOfRange(myStatements, 0, count);
|
||||
tb = new TerminalBlock(myOperations, myVariable, Arrays.copyOfRange(myStatements, count, myStatements.length)).extractFilter();
|
||||
}
|
||||
if (tb == null || !ControlFlowUtils.statementBreaksLoop(tb.getSingleStatement(), getMainLoop())) return this;
|
||||
PsiStatement sourceStatement = getStreamSourceStatement();
|
||||
if (tb == null || (sourceStatement instanceof PsiLoopStatement && !ControlFlowUtils.statementBreaksLoop(tb.getSingleStatement(),
|
||||
(PsiLoopStatement)sourceStatement))) return this;
|
||||
FilterOp filter = tb.getLastOperation(FilterOp.class);
|
||||
if (filter == null) return this;
|
||||
PsiBinaryExpression binOp = tryCast(PsiUtil.skipParenthesizedExprDown(filter.getExpression()), PsiBinaryExpression.class);
|
||||
@@ -323,7 +331,7 @@ class TerminalBlock {
|
||||
PsiExpressionList argumentList = initializer.getArgumentList();
|
||||
if (argumentList == null ||
|
||||
argumentList.getExpressions().length != 0 ||
|
||||
ControlFlowUtils.getInitializerUsageStatus(var, getMainLoop()) == ControlFlowUtils.InitializerUsageStatus.UNKNOWN) {
|
||||
ControlFlowUtils.getInitializerUsageStatus(var, getStreamSourceStatement()) == ControlFlowUtils.InitializerUsageStatus.UNKNOWN) {
|
||||
return null;
|
||||
}
|
||||
return var;
|
||||
@@ -428,8 +436,11 @@ class TerminalBlock {
|
||||
return StreamEx.ofReversed(myOperations);
|
||||
}
|
||||
|
||||
PsiLoopStatement getMainLoop() {
|
||||
return ((StreamSource)myOperations[0]).getLoop();
|
||||
/**
|
||||
* @return generally {@link PsiLoopStatement} - main loop
|
||||
*/
|
||||
PsiStatement getStreamSourceStatement() {
|
||||
return ((StreamSource)myOperations[0]).getMainStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -494,7 +505,17 @@ class TerminalBlock {
|
||||
|
||||
@NotNull
|
||||
static TerminalBlock from(StreamSource source, @NotNull PsiStatement body) {
|
||||
return new TerminalBlock(null, source, source.myVariable, body).extractOperations().tryPeelLimit(false).tryExtractDistinct();
|
||||
return fromStatements(source, body);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static TerminalBlock fromStatements(StreamSource source, @NotNull PsiStatement... statements) {
|
||||
return new TerminalBlock(null, source, source.myVariable, statements).extractOperations().tryPeelLimit(false).tryExtractDistinct();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static TerminalBlock from(StreamSource source, @NotNull PsiCodeBlock block) {
|
||||
return fromStatements(source, block.getStatements());
|
||||
}
|
||||
|
||||
boolean dependsOn(PsiExpression qualifier) {
|
||||
|
||||
+3
-3
@@ -35,7 +35,7 @@ public class ToArrayMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
@Override
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) {
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
|
||||
PsiLocalVariable arrayVariable = StreamApiMigrationInspection.extractArray(tb);
|
||||
if(arrayVariable == null) return null;
|
||||
PsiAssignmentExpression assignment = tb.getSingleExpression(PsiAssignmentExpression.class);
|
||||
@@ -50,7 +50,7 @@ public class ToArrayMigration extends BaseStreamApiMigration {
|
||||
if(loop == null) return null;
|
||||
PsiArrayType arrayType = tryCast(initializer.getType(), PsiArrayType.class);
|
||||
if(arrayType == null) return null;
|
||||
InitializerUsageStatus status = ControlFlowUtils.getInitializerUsageStatus(arrayVariable, tb.getMainLoop());
|
||||
InitializerUsageStatus status = ControlFlowUtils.getInitializerUsageStatus(arrayVariable, tb.getStreamSourceStatement());
|
||||
if(status == ControlFlowUtils.InitializerUsageStatus.UNKNOWN) return null;
|
||||
PsiType componentType = arrayType.getComponentType();
|
||||
String supplier;
|
||||
@@ -61,6 +61,6 @@ public class ToArrayMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
MapOp mapping = new MapOp(rValue, tb.getVariable(), assignment.getType());
|
||||
String replacementText = loop.withBound(dimension).createReplacement() + mapping.createReplacement() + ".toArray(" + supplier + ")";
|
||||
return replaceInitializer(tb.getMainLoop(), arrayVariable, initializer, replacementText, status);
|
||||
return replaceInitializer(tb.getStreamSourceStatement(), arrayVariable, initializer, replacementText, status);
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Extract intermediate operations" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> other = new ArrayList<>();
|
||||
other.stream().filter(s -> s.length() > 2).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Extract intermediate operations" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> other = new ArrayList<>();
|
||||
// c1
|
||||
//c2
|
||||
other.stream().filter(s -> s.length() > 2).forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Main {
|
||||
private void test(List<String> strs) {
|
||||
List<String> other = strs.stream().filter(s -> s.length() > 2).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs;
|
||||
List<String> other = new ArrayList<>();
|
||||
strs = other.stream().collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs;
|
||||
List<String> other = new ArrayList<>();
|
||||
strs = other.stream().filter(s -> s.length() > 2).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace with toArray" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> other = new ArrayList<>();
|
||||
String[] arr = other.stream().filter(s -> s.length() > 2).sorted(String.CASE_INSENSITIVE_ORDER).toArray();
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs;
|
||||
List<String> other = new ArrayList<>();
|
||||
strs = other.stream().filter(s -> s.length() > 2).sorted(String::compareTo).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Extract intermediate operations" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> other = new ArrayList<>();
|
||||
other.stream().filter(s -> s.length() > 2).forEachOrdered(System.out::println);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Main {
|
||||
private void test(List<String> other) {
|
||||
List<String> strs = other.stream().collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs;
|
||||
List<String> other = new ArrayList<>();
|
||||
strs = other.stream().collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace with count()" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs = new ArrayList<>();
|
||||
int count = (int) strs.stream().count();
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs = new ArrayList<>();
|
||||
String sb = strs.stream().collect(Collectors.joining());
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Replace with toArray" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test(List<String> strs) {
|
||||
String[] arr = strs.toArray();
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> other = new ArrayList<>();
|
||||
HashMap<Integer, String> map = other.stream().collect(Collectors.toMap(String::length, s -> s, (a, b) -> a, HashMap::new));
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Extract intermediate operations" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> other = new ArrayList<>();
|
||||
other.stream().forEach<caret>(s -> {
|
||||
if(s.length() > 2) System.out.println(s);
|
||||
});
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Extract intermediate operations" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> other = new ArrayList<>();
|
||||
other.stream().forEach<caret>(s -> { // c1
|
||||
if(s.length() > 2) System.out.println(s); //c2
|
||||
});
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test(List<String> strs) {
|
||||
List<String> other = new ArrayList<>();
|
||||
strs.stream().forEach<caret>(s -> {
|
||||
if(s.length() > 2) {
|
||||
other.add(s);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs = new ArrayList<>();
|
||||
List<String> other = new ArrayList<>();
|
||||
other.stream().forEach<caret>(s -> {strs.add(s)});
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs = new ArrayList<>();
|
||||
List<String> other = new ArrayList<>();
|
||||
other.stream().forEach<caret>(s -> {
|
||||
if(s.length() > 2) strs.add(s);
|
||||
});
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// "Replace with toArray" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs = new ArrayList<>();
|
||||
List<String> other = new ArrayList<>();
|
||||
other.stream().forEach<caret>(s -> {
|
||||
if(s.length() > 2) {
|
||||
strs.add(s);
|
||||
}
|
||||
});
|
||||
String[] arr = strs.toArray();
|
||||
Arrays.sort(arr, String.CASE_INSENSITIVE_ORDER);
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs = new ArrayList<>();
|
||||
List<String> other = new ArrayList<>();
|
||||
other.stream().forEach<caret>(s -> {
|
||||
if(s.length() > 2) {
|
||||
strs.add(s);
|
||||
}
|
||||
});
|
||||
strs.sort(String::compareTo);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Extract intermediate operations" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> other = new ArrayList<>();
|
||||
other.stream().forEachOrdered<caret>(s -> {if(s.length() > 2) System.out.println(s);});
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test(List<String> other) {
|
||||
List<String> strs = new ArrayList<>();
|
||||
other.stream().forEach<caret>(s -> {strs.add(s)});
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs = new ArrayList<>();
|
||||
List<String> other = new ArrayList<>();
|
||||
other.stream().forEach<caret>(s -> strs.add(s));
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with count()" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs = new ArrayList<>();
|
||||
int count = 0;
|
||||
strs.stream().forEach<caret>(x -> count++)
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> strs = new ArrayList<>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
strs.stream().forEach<caret>(x -> sb.append(x));
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with toArray" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test(List<String> strs) {
|
||||
List<String> tmp = new ArrayList<>();
|
||||
strs.stream().forEach<caret>(x -> tmp.add(x));
|
||||
String[] arr = tmp.toArray();
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
private void test() {
|
||||
List<String> other = new ArrayList<>();
|
||||
HashMap<Integer, String> map = new HashMap<Integer, String>();
|
||||
other.stream().forEach<caret>(s -> map.putIfAbsent(s.length(), s));
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.java.codeInsight.daemon.quickFix;
|
||||
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.streamMigration.SimplifyForEachInspection;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SimplifyForEachInspectionTest extends LightQuickFixParameterizedTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new SimplifyForEachInspection()};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LanguageLevel getDefaultLanguageLevel() {
|
||||
return LanguageLevel.JDK_1_8;
|
||||
}
|
||||
|
||||
public void test() { doAllTests(); }
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/simplifyForEach";
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -765,7 +765,7 @@ public class ControlFlowUtils {
|
||||
*/
|
||||
private static boolean isVariableReferencedBeforeLoopEntry(final ControlFlow flow,
|
||||
final int start,
|
||||
final PsiLoopStatement loop,
|
||||
final PsiStatement loop,
|
||||
final PsiVariable variable) {
|
||||
final int loopStart = flow.getStartOffset(loop);
|
||||
final int loopEnd = flow.getEndOffset(loop);
|
||||
@@ -812,7 +812,7 @@ public class ControlFlowUtils {
|
||||
* @return initializer usage status for variable
|
||||
*/
|
||||
@NotNull
|
||||
public static InitializerUsageStatus getInitializerUsageStatus(PsiVariable var, PsiLoopStatement loop) {
|
||||
public static InitializerUsageStatus getInitializerUsageStatus(PsiVariable var, PsiStatement loop) {
|
||||
if(!(var instanceof PsiLocalVariable) || var.getInitializer() == null) return UNKNOWN;
|
||||
if(isDeclarationJustBefore(var, loop)) return DECLARED_JUST_BEFORE;
|
||||
// Check that variable is declared in the same method or the same lambda expression
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
This inspection reports forEach which can be replaced with more concise method or intermediate steps can be extracted.
|
||||
</p>
|
||||
Stream API is not available under Java 1.7 or earlier JVMs
|
||||
<small>New in 2017.3</small>
|
||||
</body>
|
||||
</html>
|
||||
@@ -805,6 +805,9 @@
|
||||
<localInspection groupPath="Java,Java language level migration aids" language="JAVA" shortName="Convert2streamapi" displayName="Loop can be collapsed with Stream API"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids8" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="INFORMATION"
|
||||
implementationClass="com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection"/>
|
||||
<localInspection groupPath="Java,Java language level migration aids" language="JAVA" shortName="SimplifyForEach" displayName="forEach call can be simplified"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids8" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.streamMigration.SimplifyForEachInspection"/>
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="StreamToLoop" displayName="Stream API call chain can be replaced with loop"
|
||||
groupKey="group.names.code.style.issues" groupBundle="messages.InspectionsBundle" enabledByDefault="true" level="INFORMATION"
|
||||
implementationClass="com.intellij.codeInspection.streamToLoop.StreamToLoopInspection"/>
|
||||
|
||||
Reference in New Issue
Block a user