mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
StreamApiMigrationInspection: refactoring of collect scenarios: CollectTerminal interface extracted and used; all forEach scenarios moved to ForEachMigration; cosmetics
This commit is contained in:
+580
-225
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -21,16 +21,26 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.SuggestedNameInfo;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
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.siyeh.ig.psiutils.EquivalenceChecker;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.VariableAccessUtils;
|
||||
import one.util.streamex.EntryStream;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.isCallOf;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
@@ -42,7 +52,7 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
PsiType getAddedElementType(PsiMethodCallExpression call) {
|
||||
static PsiType getAddedElementType(PsiMethodCallExpression call) {
|
||||
JavaResolveResult resolveResult = call.resolveMethodGenerics();
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if(method == null) return null;
|
||||
@@ -57,146 +67,28 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
PsiMethodCallExpression call = tb.getSingleMethodCall();
|
||||
if (call == null) return null;
|
||||
|
||||
CollectTerminal terminal = extractCollectTerminal(tb);
|
||||
if (terminal == null) return null;
|
||||
PsiVariable variable = terminal.getTargetVariable();
|
||||
LOG.assertTrue(variable != null);
|
||||
String stream = tb.generate() + terminal.generateIntermediate() + terminal.generateTerminal();
|
||||
InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(variable, loopStatement);
|
||||
if (status == InitializerUsageStatus.UNKNOWN) return null;
|
||||
PsiElement toReplace = terminal.getElementToReplace();
|
||||
restoreComments(loopStatement, body);
|
||||
PsiExpression qualifierExpression = call.getMethodExpression().getQualifierExpression();
|
||||
PsiLocalVariable variable = StreamApiMigrationInspection.extractCollectionVariable(qualifierExpression);
|
||||
if(variable != null && InheritanceUtil.isInheritor(variable.getType(), CommonClassNames.JAVA_UTIL_MAP)) {
|
||||
PsiElement result = handleToMap(loopStatement, tb, call, variable);
|
||||
if (result != null) return result;
|
||||
}
|
||||
|
||||
PsiExpression itemToAdd = call.getArgumentList().getExpressions()[0];
|
||||
PsiType addedType = getAddedElementType(call);
|
||||
if (addedType == null) addedType = itemToAdd.getType();
|
||||
|
||||
if(variable == null && qualifierExpression instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression qualifierCall = (PsiMethodCallExpression)qualifierExpression;
|
||||
if(StreamApiMigrationInspection.isCallOf(qualifierCall, CommonClassNames.JAVA_UTIL_MAP, "computeIfAbsent")) {
|
||||
PsiElement result = handleComputeIfAbsent(loopStatement, tb, itemToAdd, qualifierCall);
|
||||
if (result != null) return result;
|
||||
PsiElement result;
|
||||
if (toReplace != null) {
|
||||
result = toReplace.replace(factory.createExpressionFromText(stream, toReplace));
|
||||
removeLoop(loopStatement);
|
||||
if (status != InitializerUsageStatus.AT_WANTED_PLACE) {
|
||||
variable.delete();
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder(tb.add(new MapOp(itemToAdd, tb.getVariable(), addedType)).generate());
|
||||
|
||||
if (variable != null) {
|
||||
InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(variable, loopStatement);
|
||||
if(status != InitializerUsageStatus.UNKNOWN) {
|
||||
PsiExpression initializer = variable.getInitializer();
|
||||
LOG.assertTrue(initializer != null);
|
||||
PsiElement toArrayConversion = handleToArray(builder, initializer, loopStatement, call);
|
||||
if(toArrayConversion != null) {
|
||||
if(status != InitializerUsageStatus.AT_WANTED_PLACE) {
|
||||
variable.delete();
|
||||
}
|
||||
return toArrayConversion;
|
||||
}
|
||||
PsiElement nextStatement = PsiTreeUtil.skipSiblingsForward(loopStatement, PsiComment.class, PsiWhiteSpace.class);
|
||||
String comparatorText = tryExtractSortComparatorText(nextStatement, variable);
|
||||
if(comparatorText != null) {
|
||||
builder.append(".sorted(").append(comparatorText).append(")");
|
||||
nextStatement.delete();
|
||||
}
|
||||
String callText = builder.append(".collect(" + CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + ".")
|
||||
.append(createInitializerReplacementText(qualifierExpression.getType(), initializer))
|
||||
.append(")").toString();
|
||||
return replaceInitializer(loopStatement, variable, initializer, callText, status);
|
||||
}
|
||||
else {
|
||||
result = replaceInitializer(loopStatement, variable, variable.getInitializer(), stream, status);
|
||||
}
|
||||
String qualifierText = qualifierExpression != null ? qualifierExpression.getText() + "." : "";
|
||||
|
||||
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
|
||||
SuggestedNameInfo suggestedNameInfo =
|
||||
codeStyleManager.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, addedType, false);
|
||||
if (suggestedNameInfo.names.length == 0) {
|
||||
suggestedNameInfo = codeStyleManager.suggestVariableName(VariableKind.LOCAL_VARIABLE, "item", null, null, false);
|
||||
}
|
||||
String varName = codeStyleManager.suggestUniqueVariableName(suggestedNameInfo, call, false).names[0];
|
||||
|
||||
PsiExpression forEachBody =
|
||||
factory.createExpressionFromText(qualifierText + "add(" + varName + ")", qualifierExpression);
|
||||
String callText = builder.append(".forEach(").append(varName).append("->").append(forEachBody.getText()).append(");").toString();
|
||||
return loopStatement.replace(factory.createStatementFromText(callText, loopStatement));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement handleComputeIfAbsent(@NotNull PsiLoopStatement loopStatement, @NotNull TerminalBlock tb,
|
||||
PsiExpression itemToAdd, PsiMethodCallExpression qualifierCall) {
|
||||
PsiLocalVariable variable;
|
||||
variable = StreamApiMigrationInspection.extractCollectionVariable(qualifierCall.getMethodExpression().getQualifierExpression());
|
||||
if (variable == null || !InheritanceUtil.isInheritor(variable.getType(), CommonClassNames.JAVA_UTIL_MAP)) return null;
|
||||
InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(variable, loopStatement);
|
||||
if(status == InitializerUsageStatus.UNKNOWN) return null;
|
||||
PsiExpression[] computeArgs = qualifierCall.getArgumentList().getExpressions();
|
||||
if(!(computeArgs[1] instanceof PsiLambdaExpression)) return null;
|
||||
PsiExpression ctor = LambdaUtil.extractSingleExpressionFromBody(((PsiLambdaExpression)computeArgs[1]).getBody());
|
||||
PsiType mapType = variable.getType();
|
||||
PsiType valueType = PsiUtil.substituteTypeParameter(mapType, CommonClassNames.JAVA_UTIL_MAP, 1, false);
|
||||
if(valueType == null) return null;
|
||||
String downstreamCollector = CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + "." + createInitializerReplacementText(valueType, ctor);
|
||||
if(!ExpressionUtils.isReferenceTo(itemToAdd, tb.getVariable())) {
|
||||
downstreamCollector = CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + ".mapping(" +
|
||||
tb.getVariable().getName() + "->" + itemToAdd.getText() + "," + downstreamCollector + ")";
|
||||
}
|
||||
StringBuilder builder = new StringBuilder(tb.generate());
|
||||
builder.append(".collect(" + CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + ".groupingBy(")
|
||||
.append(LambdaUtil.createLambda(tb.getVariable(), computeArgs[0]));
|
||||
PsiExpression initializer = variable.getInitializer();
|
||||
LOG.assertTrue(initializer != null);
|
||||
if (!isHashMap(variable)) {
|
||||
builder.append(",()->").append(initializer.getText()).append(",").append(downstreamCollector);
|
||||
}
|
||||
else if (!(CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + "." + "toList()").equals(downstreamCollector)) {
|
||||
builder.append(",").append(downstreamCollector);
|
||||
}
|
||||
builder.append("))");
|
||||
return replaceInitializer(loopStatement, variable, initializer, builder.toString(), status);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement handleToMap(@NotNull PsiLoopStatement loopStatement,
|
||||
@NotNull TerminalBlock tb,
|
||||
PsiMethodCallExpression call,
|
||||
PsiLocalVariable variable) {
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
if(args.length < 2) return null;
|
||||
String methodName = call.getMethodExpression().getReferenceName();
|
||||
if(methodName == null) return null;
|
||||
InitializerUsageStatus status = StreamApiMigrationInspection.getInitializerUsageStatus(variable, loopStatement);
|
||||
if(status == InitializerUsageStatus.UNKNOWN) return null;
|
||||
Project project = loopStatement.getProject();
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
PsiExpression merger;
|
||||
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
|
||||
String aVar = codeStyleManager.suggestUniqueVariableName("a", call, true);
|
||||
String bVar = codeStyleManager.suggestUniqueVariableName("b", call, true);
|
||||
switch(methodName) {
|
||||
case "put":
|
||||
merger = factory.createExpressionFromText("("+aVar+","+bVar+")->"+bVar, call);
|
||||
break;
|
||||
case "putIfAbsent":
|
||||
merger = factory.createExpressionFromText("("+aVar+","+bVar+")->"+aVar, call);
|
||||
break;
|
||||
case "merge":
|
||||
if(args.length != 3) return null;
|
||||
merger = args[2];
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
StringBuilder collector = new StringBuilder(CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS+".toMap(");
|
||||
collector.append(LambdaUtil.createLambda(tb.getVariable(), args[0])).append(',')
|
||||
.append(LambdaUtil.createLambda(tb.getVariable(), args[1])).append(',')
|
||||
.append(merger.getText());
|
||||
PsiExpression initializer = variable.getInitializer();
|
||||
LOG.assertTrue(initializer != null);
|
||||
if(!isHashMap(variable)) {
|
||||
collector.append(",()->").append(initializer.getText());
|
||||
}
|
||||
collector.append(")");
|
||||
String callText = tb.generate() + ".collect(" + collector + ")";
|
||||
return replaceInitializer(loopStatement, variable, initializer, callText, status);
|
||||
terminal.cleanUp();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isHashMap(PsiLocalVariable variable) {
|
||||
@@ -211,93 +103,556 @@ class CollectMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement handleToArray(StringBuilder builder,
|
||||
PsiExpression initializer,
|
||||
PsiLoopStatement loopStatement,
|
||||
PsiMethodCallExpression methodCallExpression) {
|
||||
PsiMethodCallExpression toArrayExpression =
|
||||
StreamApiMigrationInspection.extractToArrayExpression(loopStatement, methodCallExpression);
|
||||
if (toArrayExpression == null) return null;
|
||||
PsiType type = initializer.getType();
|
||||
if (!(type instanceof PsiClassType)) return null;
|
||||
String replacement = StreamApiMigrationInspection.COLLECTION_TO_ARRAY.get(((PsiClassType)type).rawType().getCanonicalText());
|
||||
if (replacement == null) return null;
|
||||
static PsiLocalVariable extractQualifierVariable(TerminalBlock tb, PsiMethodCallExpression call) {
|
||||
PsiExpression qualifierExpression = PsiUtil.skipParenthesizedExprDown(call.getMethodExpression().getQualifierExpression());
|
||||
if (!(qualifierExpression instanceof PsiReferenceExpression)) return null;
|
||||
PsiElement resolve = ((PsiReferenceExpression)qualifierExpression).resolve();
|
||||
if (!(resolve instanceof PsiLocalVariable)) return null;
|
||||
PsiLocalVariable variable = (PsiLocalVariable)resolve;
|
||||
if (tb.getVariable() != variable && VariableAccessUtils.variableIsUsed(variable, call.getArgumentList())) return null;
|
||||
return variable;
|
||||
}
|
||||
|
||||
builder.append(".").append(replacement);
|
||||
PsiExpression[] args = toArrayExpression.getArgumentList().getExpressions();
|
||||
if(args.length == 0) {
|
||||
builder.append("()");
|
||||
} else {
|
||||
if(args.length != 1 || !(args[0] instanceof PsiNewExpression)) return null;
|
||||
@Nullable
|
||||
static CollectTerminal extractCollectTerminal(TerminalBlock tb) {
|
||||
PsiMethodCallExpression call = tb.getSingleMethodCall();
|
||||
if (call == null) return null;
|
||||
PsiReferenceExpression methodExpression = call.getMethodExpression();
|
||||
PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
|
||||
if (tb.dependsOn(qualifierExpression)) return null;
|
||||
|
||||
List<BiFunction<TerminalBlock, PsiMethodCallExpression, CollectTerminal>> extractors =
|
||||
Arrays.asList(AddingTerminal::tryExtract, GroupingTerminal::tryExtract, ToMapTerminal::tryExtract);
|
||||
|
||||
CollectTerminal terminal = StreamEx.of(extractors).map(extractor -> extractor.apply(tb, call)).nonNull().findFirst().orElse(null);
|
||||
if (terminal != null) {
|
||||
PsiVariable variable = terminal.getTargetVariable();
|
||||
if (variable == null || variable.getInitializer() == null) return null;
|
||||
terminal = includePostStatements(terminal, tb.getMainLoop());
|
||||
}
|
||||
return terminal;
|
||||
}
|
||||
|
||||
static CollectTerminal includePostStatements(CollectTerminal terminal, PsiLoopStatement loop) {
|
||||
List<BiFunction<CollectTerminal, PsiElement, CollectTerminal>> wrappers =
|
||||
Arrays.asList(SortingTerminal::tryWrap, (t, e) -> ToArrayTerminal.tryWrap(t, loop, e));
|
||||
PsiElement nextStatement = loop;
|
||||
while (true) {
|
||||
nextStatement = PsiTreeUtil.skipSiblingsForward(nextStatement, PsiComment.class, PsiWhiteSpace.class);
|
||||
CollectTerminal wrapped = null;
|
||||
for (BiFunction<CollectTerminal, PsiElement, CollectTerminal> wrapper : wrappers) {
|
||||
wrapped = wrapper.apply(terminal, nextStatement);
|
||||
if (wrapped != null) {
|
||||
terminal = wrapped;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (wrapped == null) {
|
||||
return terminal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface CollectTerminal {
|
||||
@Nullable
|
||||
default PsiElement getElementToReplace() { return null; }
|
||||
|
||||
default String getMethodName() { return "collect"; }
|
||||
|
||||
@Nullable
|
||||
PsiVariable getTargetVariable();
|
||||
|
||||
default String generateIntermediate() { return ""; }
|
||||
|
||||
String generateTerminal();
|
||||
|
||||
default void cleanUp() {}
|
||||
|
||||
default boolean isTrivial() {
|
||||
return generateIntermediate().isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
static class AddingTerminal implements CollectTerminal {
|
||||
private @Nullable PsiVariable myTarget;
|
||||
private final PsiType myTargetType;
|
||||
private final PsiExpression myInitializer;
|
||||
private final PsiVariable myElement;
|
||||
private final PsiMethodCallExpression myAddCall;
|
||||
|
||||
AddingTerminal(@NotNull PsiVariable target,
|
||||
PsiVariable element,
|
||||
PsiMethodCallExpression addCall) {
|
||||
this(target.getType(), target.getInitializer(), element, addCall);
|
||||
if (myInitializer instanceof PsiNewExpression) {
|
||||
final PsiExpressionList argumentList = ((PsiNewExpression)myInitializer).getArgumentList();
|
||||
if (argumentList != null && argumentList.getExpressions().length == 0) {
|
||||
myTarget = target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AddingTerminal(@NotNull PsiType targetType,
|
||||
PsiExpression initializer,
|
||||
PsiVariable element,
|
||||
PsiMethodCallExpression addCall) {
|
||||
myTarget = null;
|
||||
myTargetType = targetType;
|
||||
myInitializer = initializer;
|
||||
myElement = element;
|
||||
myAddCall = addCall;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiVariable getTargetVariable() {
|
||||
return myTarget;
|
||||
}
|
||||
|
||||
PsiVariable getElementVariable() {
|
||||
return myElement;
|
||||
}
|
||||
|
||||
PsiExpression getMapping() {
|
||||
return myAddCall.getArgumentList().getExpressions()[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateIntermediate() {
|
||||
PsiType addedType = getAddedElementType(myAddCall);
|
||||
PsiExpression mapping = getMapping();
|
||||
if (addedType == null) addedType = mapping.getType();
|
||||
return new MapOp(mapping, myElement, addedType).createReplacement();
|
||||
}
|
||||
|
||||
public String generateCollector() {
|
||||
String collector;
|
||||
PsiType initializerType = myInitializer.getType();
|
||||
PsiClassType rawType = initializerType instanceof PsiClassType ? ((PsiClassType)initializerType).rawType() : null;
|
||||
PsiClassType rawVarType = myTargetType instanceof PsiClassType ? ((PsiClassType)myTargetType).rawType() : null;
|
||||
if (rawType != null && rawVarType != null &&
|
||||
rawType.equalsToText(CommonClassNames.JAVA_UTIL_ARRAY_LIST) &&
|
||||
(rawVarType.equalsToText(CommonClassNames.JAVA_UTIL_LIST) || rawVarType.equalsToText(CommonClassNames.JAVA_UTIL_COLLECTION))) {
|
||||
collector = "toList()";
|
||||
}
|
||||
else if (rawType != null && rawVarType != null &&
|
||||
rawType.equalsToText(CommonClassNames.JAVA_UTIL_HASH_SET) &&
|
||||
(rawVarType.equalsToText(CommonClassNames.JAVA_UTIL_SET) ||
|
||||
rawVarType.equalsToText(CommonClassNames.JAVA_UTIL_COLLECTION))) {
|
||||
collector = "toSet()";
|
||||
}
|
||||
else {
|
||||
collector = "toCollection(() -> " + myInitializer.getText() + ")";
|
||||
}
|
||||
return CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + "." + collector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTerminal() {
|
||||
return ".collect(" + generateCollector() + ")";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static AddingTerminal tryExtract(TerminalBlock tb, PsiMethodCallExpression call) {
|
||||
if (!isCallOf(call, CommonClassNames.JAVA_UTIL_COLLECTION, "add")) return null;
|
||||
PsiExpression qualifierExpression = call.getMethodExpression().getQualifierExpression();
|
||||
if (qualifierExpression == null) return null;
|
||||
PsiExpression count = tb.getCountExpression();
|
||||
PsiLocalVariable variable = extractQualifierVariable(tb, call);
|
||||
if (variable != null) {
|
||||
AddingTerminal terminal = new AddingTerminal(variable, tb.getVariable(), call);
|
||||
if (count == null) return terminal;
|
||||
// like "list.add(x); if(list.size() >= limit) break;"
|
||||
if (!(count instanceof PsiMethodCallExpression)) return null;
|
||||
PsiMethodCallExpression sizeCall = (PsiMethodCallExpression)count;
|
||||
PsiExpression sizeQualifier = sizeCall.getMethodExpression().getQualifierExpression();
|
||||
if (isCallOf(sizeCall, CommonClassNames.JAVA_UTIL_COLLECTION, "size") &&
|
||||
EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(sizeQualifier, qualifierExpression) &&
|
||||
InheritanceUtil.isInheritor(PsiUtil.resolveClassInClassTypeOnly(variable.getType()), CommonClassNames.JAVA_UTIL_LIST)) {
|
||||
return terminal;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class GroupingTerminal implements CollectTerminal {
|
||||
private final AddingTerminal myDownstream;
|
||||
private final PsiLocalVariable myTarget;
|
||||
private final PsiExpression myKeyExpression;
|
||||
|
||||
GroupingTerminal(AddingTerminal downstream, PsiLocalVariable target, PsiExpression expression) {
|
||||
myDownstream = downstream;
|
||||
myTarget = target;
|
||||
myKeyExpression = expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrivial() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiVariable getTargetVariable() {
|
||||
return myTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTerminal() {
|
||||
String downstreamCollector = myDownstream.generateCollector();
|
||||
PsiVariable elementVariable = myDownstream.getElementVariable();
|
||||
if (!ExpressionUtils.isReferenceTo(myDownstream.getMapping(), myDownstream.getElementVariable())) {
|
||||
downstreamCollector = CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + ".mapping(" +
|
||||
myDownstream.getElementVariable().getName() + "->" + myDownstream.getMapping().getText() + "," +
|
||||
downstreamCollector + ")";
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(".collect(" + CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + ".groupingBy(")
|
||||
.append(LambdaUtil.createLambda(elementVariable, myKeyExpression));
|
||||
PsiExpression initializer = myTarget.getInitializer();
|
||||
LOG.assertTrue(initializer != null);
|
||||
if (!isHashMap(myTarget)) {
|
||||
builder.append(",()->").append(initializer.getText()).append(",").append(downstreamCollector);
|
||||
}
|
||||
else if (!(CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + "." + "toList()").equals(downstreamCollector)) {
|
||||
builder.append(",").append(downstreamCollector);
|
||||
}
|
||||
builder.append("))");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static GroupingTerminal tryExtract(TerminalBlock tb, PsiMethodCallExpression call) {
|
||||
if (!isCallOf(call, CommonClassNames.JAVA_UTIL_COLLECTION, "add")) return null;
|
||||
PsiReferenceExpression methodExpression = call.getMethodExpression();
|
||||
PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
|
||||
|
||||
if (qualifierExpression instanceof PsiMethodCallExpression && tb.getCountExpression() == null) {
|
||||
PsiMethodCallExpression qualifierCall = (PsiMethodCallExpression)qualifierExpression;
|
||||
if (isCallOf(qualifierCall, CommonClassNames.JAVA_UTIL_MAP, "computeIfAbsent")) {
|
||||
PsiExpression[] args = qualifierCall.getArgumentList().getExpressions();
|
||||
if (args.length != 2 || !(args[1] instanceof PsiLambdaExpression)) return null;
|
||||
PsiLambdaExpression lambda = (PsiLambdaExpression)args[1];
|
||||
PsiExpression body = LambdaUtil.extractSingleExpressionFromBody(lambda.getBody());
|
||||
if (!(body instanceof PsiNewExpression)) return null;
|
||||
PsiExpressionList ctorArgs = ((PsiNewExpression)body).getArgumentList();
|
||||
if (ctorArgs != null && ctorArgs.getExpressions().length == 0) {
|
||||
PsiLocalVariable variable = extractQualifierVariable(tb, qualifierCall);
|
||||
if (variable != null && variable.getInitializer() instanceof PsiNewExpression) {
|
||||
PsiType mapType = variable.getType();
|
||||
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);
|
||||
return new GroupingTerminal(adding, variable, args[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class ToMapTerminal implements CollectTerminal {
|
||||
private final PsiMethodCallExpression myMapUpdateCall;
|
||||
private final PsiLocalVariable myTargetVariable;
|
||||
private final PsiVariable myElementVariable;
|
||||
|
||||
ToMapTerminal(PsiMethodCallExpression call, PsiVariable elementVariable, PsiLocalVariable variable) {
|
||||
myMapUpdateCall = call;
|
||||
myTargetVariable = variable;
|
||||
myElementVariable = elementVariable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiVariable getTargetVariable() {
|
||||
return myTargetVariable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTerminal() {
|
||||
PsiExpression[] args = myMapUpdateCall.getArgumentList().getExpressions();
|
||||
LOG.assertTrue(args.length >= 2);
|
||||
String methodName = myMapUpdateCall.getMethodExpression().getReferenceName();
|
||||
LOG.assertTrue(methodName != null);
|
||||
Project project = myMapUpdateCall.getProject();
|
||||
String merger;
|
||||
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
|
||||
String aVar = codeStyleManager.suggestUniqueVariableName("a", myMapUpdateCall, true);
|
||||
String bVar = codeStyleManager.suggestUniqueVariableName("b", myMapUpdateCall, true);
|
||||
switch (methodName) {
|
||||
case "put":
|
||||
merger = "(" + aVar + "," + bVar + ")->" + bVar;
|
||||
break;
|
||||
case "putIfAbsent":
|
||||
merger = "(" + aVar + "," + bVar + ")->" + aVar;
|
||||
break;
|
||||
case "merge":
|
||||
LOG.assertTrue(args.length == 3);
|
||||
merger = args[2].getText();
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
StringBuilder collector = new StringBuilder(".collect(" + CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + ".toMap(");
|
||||
collector.append(LambdaUtil.createLambda(myElementVariable, args[0])).append(',')
|
||||
.append(LambdaUtil.createLambda(myElementVariable, args[1])).append(',')
|
||||
.append(merger);
|
||||
PsiExpression initializer = myTargetVariable.getInitializer();
|
||||
LOG.assertTrue(initializer != null);
|
||||
if (!isHashMap(myTargetVariable)) {
|
||||
collector.append(",()->").append(initializer.getText());
|
||||
}
|
||||
collector.append("))");
|
||||
return collector.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static ToMapTerminal tryExtract(TerminalBlock tb, PsiMethodCallExpression call) {
|
||||
if (tb.getCountExpression() != null ||
|
||||
!isCallOf(call, CommonClassNames.JAVA_UTIL_MAP, "merge", "put", "putIfAbsent")) {
|
||||
return null;
|
||||
}
|
||||
PsiLocalVariable variable = extractQualifierVariable(tb, call);
|
||||
if (variable == null) return null;
|
||||
PsiExpression initializer = variable.getInitializer();
|
||||
if (!(initializer instanceof PsiNewExpression)) return null;
|
||||
PsiExpressionList argumentList = ((PsiNewExpression)initializer).getArgumentList();
|
||||
if (argumentList == null || argumentList.getExpressions().length != 0) return null;
|
||||
return new ToMapTerminal(call, tb.getVariable(), variable);
|
||||
}
|
||||
}
|
||||
|
||||
static class SortingTerminal implements CollectTerminal {
|
||||
private final CollectTerminal myDownstream;
|
||||
private final PsiExpression myComparator;
|
||||
private final PsiStatement myStatement;
|
||||
|
||||
SortingTerminal(CollectTerminal downstream, PsiStatement statement, PsiExpression comparator) {
|
||||
myDownstream = downstream;
|
||||
myStatement = statement;
|
||||
myComparator = comparator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodName() {
|
||||
return myDownstream.getMethodName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiVariable getTargetVariable() {
|
||||
return myDownstream.getTargetVariable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateIntermediate() {
|
||||
return myDownstream.generateIntermediate() + ".sorted("
|
||||
+ (myComparator == null ? "" : myComparator.getText()) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTerminal() {
|
||||
return myDownstream.generateTerminal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
myDownstream.cleanUp();
|
||||
myStatement.delete();
|
||||
}
|
||||
|
||||
@Contract("null, _ -> null")
|
||||
public static CollectTerminal tryWrap(CollectTerminal terminal, PsiElement element) {
|
||||
PsiVariable list = terminal.getTargetVariable();
|
||||
if (list == null || !(element instanceof PsiExpressionStatement)) return null;
|
||||
PsiExpression expression = ((PsiExpressionStatement)element).getExpression();
|
||||
if (!(expression instanceof PsiMethodCallExpression)) return null;
|
||||
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
|
||||
PsiReferenceExpression methodExpression = methodCall.getMethodExpression();
|
||||
if (!"sort".equals(methodExpression.getReferenceName())) return null;
|
||||
PsiMethod method = methodCall.resolveMethod();
|
||||
if (method == null) return null;
|
||||
PsiClass containingClass = method.getContainingClass();
|
||||
if (containingClass == null) return null;
|
||||
PsiExpression listExpression = null;
|
||||
PsiExpression comparatorExpression = null;
|
||||
if (CommonClassNames.JAVA_UTIL_COLLECTIONS.equals(containingClass.getQualifiedName())) {
|
||||
PsiExpression[] args = methodCall.getArgumentList().getExpressions();
|
||||
if (args.length == 1) {
|
||||
listExpression = args[0];
|
||||
}
|
||||
else if (args.length == 2) {
|
||||
listExpression = args[0];
|
||||
comparatorExpression = args[1];
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (InheritanceUtil.isInheritor(containingClass, CommonClassNames.JAVA_UTIL_LIST)) {
|
||||
listExpression = methodExpression.getQualifierExpression();
|
||||
PsiExpression[] args = methodCall.getArgumentList().getExpressions();
|
||||
if (args.length != 1) return null;
|
||||
comparatorExpression = args[0];
|
||||
}
|
||||
if (!(listExpression instanceof PsiReferenceExpression) || !((PsiReferenceExpression)listExpression).isReferenceTo(list)) {
|
||||
return null;
|
||||
}
|
||||
if (ExpressionUtils.isNullLiteral(comparatorExpression)) {
|
||||
comparatorExpression = null;
|
||||
}
|
||||
return new SortingTerminal(terminal, (PsiExpressionStatement)element, comparatorExpression);
|
||||
}
|
||||
}
|
||||
|
||||
static class ToArrayTerminal implements CollectTerminal {
|
||||
static final Map<String, String> INTERMEDIATE_STEPS = EntryStream.of(
|
||||
CommonClassNames.JAVA_UTIL_ARRAY_LIST, "",
|
||||
"java.util.LinkedList", "",
|
||||
CommonClassNames.JAVA_UTIL_HASH_SET, ".distinct()",
|
||||
"java.util.LinkedHashSet", ".distinct()",
|
||||
"java.util.TreeSet", ".distinct().sorted()"
|
||||
).toMap();
|
||||
|
||||
private final CollectTerminal myUpstream;
|
||||
private final String myIntermediate;
|
||||
private final PsiMethodCallExpression myToArrayExpression;
|
||||
private final String mySupplier;
|
||||
|
||||
ToArrayTerminal(CollectTerminal upstream,
|
||||
String intermediate,
|
||||
String supplier,
|
||||
PsiMethodCallExpression toArrayExpression) {
|
||||
myUpstream = upstream;
|
||||
mySupplier = supplier;
|
||||
myIntermediate = intermediate;
|
||||
myToArrayExpression = toArrayExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrivial() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodName() {
|
||||
return "toArray";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement getElementToReplace() {
|
||||
return myToArrayExpression;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiVariable getTargetVariable() {
|
||||
return myUpstream.getTargetVariable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateIntermediate() {
|
||||
return myUpstream.generateIntermediate() + myIntermediate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTerminal() {
|
||||
return ".toArray(" + mySupplier + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp() {
|
||||
myUpstream.cleanUp();
|
||||
}
|
||||
|
||||
@Contract("null, _, _ -> null")
|
||||
public static ToArrayTerminal tryWrap(CollectTerminal terminal, PsiLoopStatement loopStatement, PsiElement element) {
|
||||
PsiVariable collectionVariable = terminal.getTargetVariable();
|
||||
if (collectionVariable == null || StreamApiMigrationInspection.getInitializerUsageStatus(collectionVariable, loopStatement)
|
||||
== InitializerUsageStatus.UNKNOWN) {
|
||||
return null;
|
||||
}
|
||||
PsiExpression initializer = collectionVariable.getInitializer();
|
||||
if (initializer == null) return null;
|
||||
PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(initializer.getType());
|
||||
if (aClass == null) return null;
|
||||
String intermediateSteps = INTERMEDIATE_STEPS.get(aClass.getQualifiedName());
|
||||
if (intermediateSteps == null) return null;
|
||||
|
||||
PsiMethodCallExpression toArrayExpression =
|
||||
extractToArrayExpression(element, loopStatement, collectionVariable);
|
||||
if (toArrayExpression == null) return null;
|
||||
PsiExpression[] args = toArrayExpression.getArgumentList().getExpressions();
|
||||
String supplier;
|
||||
if (args.length == 0) {
|
||||
supplier = "";
|
||||
}
|
||||
else {
|
||||
if (args.length != 1 || !(args[0] instanceof PsiNewExpression)) return null;
|
||||
PsiNewExpression newArray = (PsiNewExpression)args[0];
|
||||
PsiType arrayType = newArray.getType();
|
||||
if (arrayType == null) return null;
|
||||
String name = arrayType.getCanonicalText();
|
||||
supplier = name + "::new";
|
||||
}
|
||||
return new ToArrayTerminal(terminal, intermediateSteps, supplier, toArrayExpression);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static PsiMethodCallExpression extractToArrayExpression(PsiElement nextElement, PsiLoopStatement statement, PsiVariable collectionVariable) {
|
||||
// return collection.toArray() or collection.toArray(new Type[0]) or collection.toArray(new Type[collection.size()]);
|
||||
PsiExpression toArrayCandidate;
|
||||
if (nextElement instanceof PsiReturnStatement) {
|
||||
toArrayCandidate = ((PsiReturnStatement)nextElement).getReturnValue();
|
||||
}
|
||||
else {
|
||||
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(nextElement);
|
||||
if (assignment != null) {
|
||||
toArrayCandidate = assignment.getRExpression();
|
||||
}
|
||||
else if (nextElement instanceof PsiDeclarationStatement) {
|
||||
PsiElement[] elements = ((PsiDeclarationStatement)nextElement).getDeclaredElements();
|
||||
if (elements.length == 1 && elements[0] instanceof PsiLocalVariable) {
|
||||
toArrayCandidate = ((PsiLocalVariable)elements[0]).getInitializer();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (!(toArrayCandidate instanceof PsiMethodCallExpression)) return null;
|
||||
PsiMethodCallExpression call = (PsiMethodCallExpression)toArrayCandidate;
|
||||
PsiReferenceExpression methodExpression = call.getMethodExpression();
|
||||
if (!"toArray".equals(methodExpression.getReferenceName())) return null;
|
||||
if (!ExpressionUtils.isReferenceTo(methodExpression.getQualifierExpression(), collectionVariable)) return null;
|
||||
|
||||
if (!(nextElement instanceof PsiReturnStatement) && !ReferencesSearch.search(collectionVariable)
|
||||
.forEach(ref ->
|
||||
ref.getElement() == collectionVariable || PsiTreeUtil.isAncestor(statement, ref.getElement(), false) ||
|
||||
PsiTreeUtil.isAncestor(toArrayCandidate, ref.getElement(), false)
|
||||
)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
if (args.length == 0) return call;
|
||||
if (args.length != 1 || !(args[0] instanceof PsiNewExpression)) return null;
|
||||
PsiNewExpression newArray = (PsiNewExpression)args[0];
|
||||
PsiType arrayType = newArray.getType();
|
||||
if(arrayType == null) return null;
|
||||
String name = arrayType.getCanonicalText();
|
||||
builder.append('(').append(name).append("::new)");
|
||||
PsiExpression[] dimensions = newArray.getArrayDimensions();
|
||||
if (dimensions.length != 1) return null;
|
||||
if (ExpressionUtils.isLiteral(dimensions[0], 0)) return call;
|
||||
if (!(dimensions[0] instanceof PsiMethodCallExpression)) return null;
|
||||
PsiMethodCallExpression maybeSizeCall = (PsiMethodCallExpression)dimensions[0];
|
||||
if (!isCallOf(maybeSizeCall, CommonClassNames.JAVA_UTIL_COLLECTION, "size")) return null;
|
||||
PsiExpression sizeQualifier = maybeSizeCall.getMethodExpression().getQualifierExpression();
|
||||
if (!ExpressionUtils.isReferenceTo(sizeQualifier, collectionVariable)) return null;
|
||||
return call;
|
||||
}
|
||||
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(toArrayExpression.getProject());
|
||||
PsiElement result = toArrayExpression.replace(elementFactory.createExpressionFromText(builder.toString(), toArrayExpression));
|
||||
removeLoop(loopStatement);
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String createInitializerReplacementText(PsiType varType, PsiExpression initializer) {
|
||||
final PsiType initializerType = initializer.getType();
|
||||
final PsiClassType rawType = initializerType instanceof PsiClassType ? ((PsiClassType)initializerType).rawType() : null;
|
||||
final PsiClassType rawVarType = varType instanceof PsiClassType ? ((PsiClassType)varType).rawType() : null;
|
||||
if (rawType != null && rawVarType != null &&
|
||||
rawType.equalsToText(CommonClassNames.JAVA_UTIL_ARRAY_LIST) &&
|
||||
(rawVarType.equalsToText(CommonClassNames.JAVA_UTIL_LIST) || rawVarType.equalsToText(CommonClassNames.JAVA_UTIL_COLLECTION))) {
|
||||
return "toList()";
|
||||
}
|
||||
else if (rawType != null && rawVarType != null &&
|
||||
rawType.equalsToText(CommonClassNames.JAVA_UTIL_HASH_SET) &&
|
||||
(rawVarType.equalsToText(CommonClassNames.JAVA_UTIL_SET) ||
|
||||
rawVarType.equalsToText(CommonClassNames.JAVA_UTIL_COLLECTION))) {
|
||||
return "toSet()";
|
||||
}
|
||||
else {
|
||||
return "toCollection(() -> " + initializer.getText() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param element sort statement candidate (must be PsiExpressionStatement)
|
||||
* @param list list which should be sorted
|
||||
* @return comparator string representation, empty string if natural order is used or null if given statement is not sort statement
|
||||
*/
|
||||
@Contract(value = "null, _ -> null")
|
||||
private static String tryExtractSortComparatorText(PsiElement element, PsiVariable list) {
|
||||
if(!(element instanceof PsiExpressionStatement)) return null;
|
||||
PsiExpression expression = ((PsiExpressionStatement)element).getExpression();
|
||||
if(!(expression instanceof PsiMethodCallExpression)) return null;
|
||||
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
|
||||
PsiReferenceExpression methodExpression = methodCall.getMethodExpression();
|
||||
if(!"sort".equals(methodExpression.getReferenceName())) return null;
|
||||
PsiMethod method = methodCall.resolveMethod();
|
||||
if(method == null) return null;
|
||||
PsiClass containingClass = method.getContainingClass();
|
||||
if(containingClass == null) return null;
|
||||
PsiExpression listExpression = null;
|
||||
PsiExpression comparatorExpression = null;
|
||||
if(CommonClassNames.JAVA_UTIL_COLLECTIONS.equals(containingClass.getQualifiedName())) {
|
||||
PsiExpression[] args = methodCall.getArgumentList().getExpressions();
|
||||
if(args.length == 1) {
|
||||
listExpression = args[0];
|
||||
} else if(args.length == 2) {
|
||||
listExpression = args[0];
|
||||
comparatorExpression = args[1];
|
||||
} else return null;
|
||||
} else if(InheritanceUtil.isInheritor(containingClass, CommonClassNames.JAVA_UTIL_LIST)) {
|
||||
listExpression = methodExpression.getQualifierExpression();
|
||||
PsiExpression[] args = methodCall.getArgumentList().getExpressions();
|
||||
if(args.length != 1) return null;
|
||||
comparatorExpression = args[0];
|
||||
}
|
||||
if(!(listExpression instanceof PsiReferenceExpression) || !((PsiReferenceExpression)listExpression).isReferenceTo(list)) return null;
|
||||
if(comparatorExpression == null || ExpressionUtils.isNullLiteral(comparatorExpression)) return "";
|
||||
return comparatorExpression.getText();
|
||||
}
|
||||
}
|
||||
|
||||
+52
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -15,11 +15,20 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.streamMigration;
|
||||
|
||||
import com.intellij.codeInspection.LambdaCanBeMethodReferenceInspection;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.SuggestedNameInfo;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.VariableAccessUtils;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.codeInspection.streamMigration.CollectMigration.getAddedElementType;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
@@ -31,18 +40,55 @@ class ForEachMigration extends BaseStreamApiMigration {
|
||||
super(forEachMethodName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static PsiExpression tryExtractMapExpression(TerminalBlock tb) {
|
||||
PsiMethodCallExpression call = tb.getSingleMethodCall();
|
||||
if(call == null) return null;
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
if(args.length != 1) return null;
|
||||
PsiExpression arg = args[0];
|
||||
if(ExpressionUtils.isReferenceTo(arg, tb.getVariable())) return null;
|
||||
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
|
||||
if(tb.dependsOn(qualifier) ||
|
||||
VariableAccessUtils.variableIsUsed(tb.getVariable(), qualifier) ||
|
||||
StreamApiMigrationInspection.isExpressionDependsOnUpdatedCollections(arg, qualifier) ||
|
||||
!LambdaCanBeMethodReferenceInspection.checkQualifier(qualifier)) return null;
|
||||
return arg;
|
||||
}
|
||||
|
||||
@Override
|
||||
PsiElement migrate(@NotNull Project project, @NotNull PsiStatement body, @NotNull TerminalBlock tb) {
|
||||
PsiLoopStatement loopStatement = tb.getMainLoop();
|
||||
restoreComments(loopStatement, body);
|
||||
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
|
||||
String stream = tb.generate(true)+"."+getReplacement()+"(";
|
||||
PsiElement block = tb.convertToElement(elementFactory);
|
||||
PsiExpression mapExpression = tryExtractMapExpression(tb);
|
||||
|
||||
if(mapExpression != null) {
|
||||
PsiMethodCallExpression call = tb.getSingleMethodCall();
|
||||
LOG.assertTrue(call != null);
|
||||
PsiType addedType = getAddedElementType(call);
|
||||
if (addedType == null) addedType = call.getType();
|
||||
JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
|
||||
SuggestedNameInfo suggestedNameInfo =
|
||||
codeStyleManager.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, addedType, false);
|
||||
if (suggestedNameInfo.names.length == 0) {
|
||||
suggestedNameInfo = codeStyleManager.suggestVariableName(VariableKind.LOCAL_VARIABLE, "item", null, null, false);
|
||||
}
|
||||
String varName = codeStyleManager.suggestUniqueVariableName(suggestedNameInfo, call, false).names[0];
|
||||
|
||||
String streamText = tb.add(new StreamApiMigrationInspection.MapOp(mapExpression, tb.getVariable(), addedType)).generate();
|
||||
String forEachBody = varName + "->" + call.getMethodExpression().getText() + "(" + varName + ")";
|
||||
String callText = streamText + "." + getReplacement() + "(" + forEachBody + ");";
|
||||
return loopStatement.replace(factory.createStatementFromText(callText, loopStatement));
|
||||
}
|
||||
|
||||
String stream = tb.generate(true) + "." + getReplacement() + "(";
|
||||
PsiElement block = tb.convertToElement(factory);
|
||||
|
||||
final String functionalExpressionText = tb.getVariable().getName() + " -> " + wrapInBlock(block);
|
||||
PsiExpressionStatement callStatement = (PsiExpressionStatement)elementFactory
|
||||
PsiExpressionStatement callStatement = (PsiExpressionStatement)factory
|
||||
.createStatementFromText(stream + functionalExpressionText + ");", loopStatement);
|
||||
callStatement = (PsiExpressionStatement)loopStatement.replace(callStatement);
|
||||
|
||||
@@ -54,7 +100,7 @@ class ForEachMigration extends BaseStreamApiMigration {
|
||||
if (expressions[0] instanceof PsiFunctionalExpression &&
|
||||
((PsiFunctionalExpression)expressions[0]).getFunctionalInterfaceType() == null) {
|
||||
callStatement =
|
||||
(PsiExpressionStatement)callStatement.replace(elementFactory.createStatementFromText(
|
||||
(PsiExpressionStatement)callStatement.replace(factory.createStatementFromText(
|
||||
stream + "(" + tb.getVariable().getText() + ") -> " + wrapInBlock(block) + ");", callStatement));
|
||||
}
|
||||
return callStatement;
|
||||
|
||||
+104
-271
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* 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.
|
||||
@@ -16,7 +16,6 @@
|
||||
package com.intellij.codeInspection.streamMigration;
|
||||
|
||||
import com.intellij.codeInsight.ExceptionUtil;
|
||||
import com.intellij.codeInsight.PsiEquivalenceUtil;
|
||||
import com.intellij.codeInsight.daemon.GroupNames;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
|
||||
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
|
||||
@@ -40,9 +39,7 @@ import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.*;
|
||||
import one.util.streamex.EntryStream;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
@@ -53,6 +50,7 @@ import javax.swing.*;
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.InitializerUsageStatus.*;
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
/**
|
||||
* User: anna
|
||||
@@ -60,14 +58,6 @@ import static com.intellij.codeInspection.streamMigration.StreamApiMigrationInsp
|
||||
public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
private static final Logger LOG = Logger.getInstance("#" + StreamApiMigrationInspection.class.getName());
|
||||
|
||||
static final Map<String, String> COLLECTION_TO_ARRAY = EntryStream.of(
|
||||
CommonClassNames.JAVA_UTIL_ARRAY_LIST, "toArray",
|
||||
"java.util.LinkedList", "toArray",
|
||||
CommonClassNames.JAVA_UTIL_HASH_SET, "distinct().toArray",
|
||||
"java.util.LinkedHashSet", "distinct().toArray",
|
||||
"java.util.TreeSet", "distinct().sorted().toArray"
|
||||
).toMap();
|
||||
|
||||
public boolean REPLACE_TRIVIAL_FOREACH;
|
||||
public boolean SUGGEST_FOREACH;
|
||||
|
||||
@@ -168,11 +158,10 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
|
||||
@Nullable
|
||||
static PsiVariable extractAccumulator(PsiAssignmentExpression assignment) {
|
||||
if(!(assignment.getLExpression() instanceof PsiReferenceExpression)) return null;
|
||||
PsiReferenceExpression lExpr = (PsiReferenceExpression)assignment.getLExpression();
|
||||
PsiElement accumulator = lExpr.resolve();
|
||||
if(!(accumulator instanceof PsiVariable)) return null;
|
||||
PsiVariable var = (PsiVariable)accumulator;
|
||||
PsiReferenceExpression lExpr = tryCast(assignment.getLExpression(), PsiReferenceExpression.class);
|
||||
if(lExpr == null) return null;
|
||||
PsiVariable var = tryCast(lExpr.resolve(), PsiVariable.class);
|
||||
if(var == null) return null;
|
||||
if(JavaTokenType.PLUSEQ.equals(assignment.getOperationTokenType())) {
|
||||
return var;
|
||||
} else if(JavaTokenType.EQ.equals(assignment.getOperationTokenType())) {
|
||||
@@ -221,13 +210,13 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
if(variables.size() != 1) return null;
|
||||
|
||||
// have single expression which is either ++x or x++ or x+=1 or x=x+1
|
||||
PsiExpression operand = extractIncrementedLValue(expression);
|
||||
if(!(operand instanceof PsiReferenceExpression)) return null;
|
||||
PsiElement element = ((PsiReferenceExpression)operand).resolve();
|
||||
PsiReferenceExpression operand = tryCast(extractIncrementedLValue(expression), PsiReferenceExpression.class);
|
||||
if(operand == null) return null;
|
||||
PsiLocalVariable variable = tryCast(operand.resolve(), PsiLocalVariable.class);
|
||||
|
||||
// the referred variable is the same as non-final variable and not used in intermediate operations
|
||||
if (element instanceof PsiLocalVariable && variables.contains(element) && !tb.isReferencedInOperations((PsiVariable)element)) {
|
||||
return (PsiLocalVariable)element;
|
||||
if (variable != null && variables.contains(variable) && !tb.isReferencedInOperations(variable)) {
|
||||
return variable;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -266,30 +255,6 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
return !(qualifierExpression instanceof PsiMethodCallExpression);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiClass extractQualifierClass(TerminalBlock tb, PsiMethodCallExpression methodCallExpression) {
|
||||
PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression();
|
||||
PsiClass qualifierClass = null;
|
||||
if (qualifierExpression instanceof PsiReferenceExpression) {
|
||||
if (VariableAccessUtils.variableIsUsed(tb.getVariable(), qualifierExpression)) {
|
||||
return null;
|
||||
}
|
||||
final PsiElement resolve = ((PsiReferenceExpression)qualifierExpression).resolve();
|
||||
if (resolve instanceof PsiVariable &&
|
||||
VariableAccessUtils.variableIsUsed((PsiVariable)resolve, methodCallExpression.getArgumentList())) {
|
||||
return null;
|
||||
}
|
||||
qualifierClass = PsiUtil.resolveClassInType(qualifierExpression.getType());
|
||||
}
|
||||
else if (qualifierExpression == null || qualifierExpression instanceof PsiThisExpression) {
|
||||
final PsiClass enclosingClass = PsiTreeUtil.getParentOfType(methodCallExpression, PsiClass.class);
|
||||
if (PsiUtil.getEnclosingStaticElement(methodCallExpression, enclosingClass) == null) {
|
||||
qualifierClass = enclosingClass;
|
||||
}
|
||||
}
|
||||
return qualifierClass;
|
||||
}
|
||||
|
||||
@Contract("null, _, _ -> false")
|
||||
static boolean isCallOf(PsiMethodCallExpression call, String className, String... methodNames) {
|
||||
if (call == null) return false;
|
||||
@@ -308,14 +273,6 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
return StreamEx.of(superMethods).map(PsiMember::getContainingClass).nonNull().map(PsiClass::getQualifiedName).has(className);
|
||||
}
|
||||
|
||||
private static boolean isCollectMapCall(TerminalBlock tb) {
|
||||
PsiMethodCallExpression call = tb.getSingleMethodCall();
|
||||
if (!isCallOf(call, CommonClassNames.JAVA_UTIL_MAP, "merge", "put", "putIfAbsent")) return false;
|
||||
PsiReferenceExpression methodExpression = call.getMethodExpression();
|
||||
PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
|
||||
return extractQualifierClass(tb, call) != null && !tb.dependsOn(qualifierExpression) && canCollect(tb.getMainLoop(), call);
|
||||
}
|
||||
|
||||
private static boolean isCountOperation(List<PsiVariable> nonFinalVariables, TerminalBlock tb) {
|
||||
PsiLocalVariable variable = getIncrementedVariable(tb.getSingleExpression(PsiExpression.class), tb, nonFinalVariables);
|
||||
PsiExpression counter = tb.getCountExpression();
|
||||
@@ -333,61 +290,14 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
getInitializerUsageStatus(variable, tb.getMainLoop()) != UNKNOWN;
|
||||
}
|
||||
|
||||
private static boolean isCollectCall(TerminalBlock tb) {
|
||||
PsiMethodCallExpression call = tb.getSingleMethodCall();
|
||||
if (!isCallOf(call, CommonClassNames.JAVA_UTIL_COLLECTION, "add")) return false;
|
||||
PsiReferenceExpression methodExpression = call.getMethodExpression();
|
||||
PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
|
||||
|
||||
if (tb.dependsOn(qualifierExpression)) return false;
|
||||
|
||||
PsiExpression count = tb.getCountExpression();
|
||||
PsiClass qualifierClass = extractQualifierClass(tb, call);
|
||||
if (qualifierClass != null) {
|
||||
if (count == null) return true;
|
||||
// like "list.add(x); if(list.size() >= limit) break;"
|
||||
if(!(count instanceof PsiMethodCallExpression)) return false;
|
||||
PsiMethodCallExpression sizeCall = (PsiMethodCallExpression)count;
|
||||
PsiExpression sizeQualifier = sizeCall.getMethodExpression().getQualifierExpression();
|
||||
return isCallOf(sizeCall, CommonClassNames.JAVA_UTIL_COLLECTION, "size") &&
|
||||
EquivalenceChecker.getCanonicalPsiEquivalence().expressionsAreEquivalent(sizeQualifier, qualifierExpression) &&
|
||||
InheritanceUtil.isInheritor(qualifierClass, CommonClassNames.JAVA_UTIL_LIST);
|
||||
}
|
||||
if (qualifierExpression instanceof PsiMethodCallExpression && count == null) {
|
||||
PsiMethodCallExpression qualifierCall = (PsiMethodCallExpression)qualifierExpression;
|
||||
if (isCallOf(qualifierCall, CommonClassNames.JAVA_UTIL_MAP, "computeIfAbsent")) {
|
||||
PsiExpression[] args = qualifierCall.getArgumentList().getExpressions();
|
||||
if (args.length != 2 || !(args[1] instanceof PsiLambdaExpression)) return false;
|
||||
PsiLambdaExpression lambda = (PsiLambdaExpression)args[1];
|
||||
PsiExpression body = LambdaUtil.extractSingleExpressionFromBody(lambda.getBody());
|
||||
if (!(body instanceof PsiNewExpression)) return false;
|
||||
PsiExpressionList ctorArgs = ((PsiNewExpression)body).getArgumentList();
|
||||
return ctorArgs != null && ctorArgs.getExpressions().length == 0 && extractQualifierClass(tb, qualifierCall) != null;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Contract("_, null -> false")
|
||||
static boolean canCollect(PsiLoopStatement statement, PsiMethodCallExpression call) {
|
||||
if(call == null) return false;
|
||||
PsiLocalVariable variable = extractCollectionVariable(call.getMethodExpression().getQualifierExpression());
|
||||
return variable != null && getInitializerUsageStatus(variable, statement) != UNKNOWN;
|
||||
}
|
||||
|
||||
@Contract("_, null -> false")
|
||||
private static boolean isTrivial(PsiStatement body, PsiLoopStatement loopStatement) {
|
||||
if(!(loopStatement instanceof PsiForeachStatement)) return false;
|
||||
PsiParameter parameter = ((PsiForeachStatement)loopStatement).getIterationParameter();
|
||||
//method reference
|
||||
private static boolean isTrivial(TerminalBlock tb) {
|
||||
PsiVariable variable = tb.getVariable();
|
||||
final PsiExpression candidate = LambdaCanBeMethodReferenceInspection
|
||||
.canBeMethodReferenceProblem(body instanceof PsiBlockStatement ? ((PsiBlockStatement)body).getCodeBlock() : body,
|
||||
new PsiParameter[]{parameter}, createDefaultConsumerType(parameter.getProject(), parameter), null);
|
||||
if (!(candidate instanceof PsiCallExpression)) {
|
||||
return true;
|
||||
}
|
||||
.canBeMethodReferenceProblem(tb.getSingleStatement(),
|
||||
new PsiVariable[]{variable}, createDefaultConsumerType(variable.getProject(), variable), null);
|
||||
if (!(candidate instanceof PsiCallExpression)) return true;
|
||||
final PsiMethod method = ((PsiCallExpression)candidate).resolveMethod();
|
||||
return method != null && isThrowsCompatible(method);
|
||||
return method == null;
|
||||
}
|
||||
|
||||
static boolean isSupported(PsiType type) {
|
||||
@@ -397,10 +307,6 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isThrowsCompatible(PsiMethod method) {
|
||||
return ContainerUtil.find(method.getThrowsList().getReferencedTypes(), type -> !ExceptionUtil.isUncheckedException(type)) != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiClassType createDefaultConsumerType(Project project, PsiVariable variable) {
|
||||
final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project);
|
||||
@@ -410,34 +316,16 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
|
||||
static boolean isVariableSuitableForStream(PsiVariable variable, PsiStatement statement, TerminalBlock tb) {
|
||||
if(ReferencesSearch.search(variable).forEach(ref -> {
|
||||
PsiElement element = ref.getElement();
|
||||
return !(element instanceof PsiExpression) ||
|
||||
!PsiUtil.isAccessedForWriting((PsiExpression)element) ||
|
||||
tb.operations().anyMatch(op -> op.isWriteAllowed(variable, (PsiExpression)element));
|
||||
PsiExpression expression = tryCast(ref.getElement(), PsiExpression.class);
|
||||
return expression == null ||
|
||||
!PsiUtil.isAccessedForWriting(expression) ||
|
||||
tb.operations().anyMatch(op -> op.isWriteAllowed(variable, expression));
|
||||
})) {
|
||||
return true;
|
||||
}
|
||||
return HighlightControlFlowUtil.isEffectivelyFinal(variable, statement, null);
|
||||
}
|
||||
|
||||
@Contract("null -> null")
|
||||
static PsiLocalVariable extractCollectionVariable(PsiExpression qualifierExpression) {
|
||||
if (qualifierExpression instanceof PsiReferenceExpression) {
|
||||
final PsiElement resolve = ((PsiReferenceExpression)qualifierExpression).resolve();
|
||||
if (resolve instanceof PsiLocalVariable) {
|
||||
PsiLocalVariable var = (PsiLocalVariable)resolve;
|
||||
final PsiExpression initializer = var.getInitializer();
|
||||
if (initializer instanceof PsiNewExpression) {
|
||||
final PsiExpressionList argumentList = ((PsiNewExpression)initializer).getArgumentList();
|
||||
if (argumentList != null && argumentList.getExpressions().length == 0) {
|
||||
return var;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether variable can be referenced between start and loop entry. Back-edges are also considered, so the actual place
|
||||
* where it referenced might be outside of (start, loop entry) interval.
|
||||
@@ -553,6 +441,46 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
return mapOp == null ? "" : "."+mapOp+"("+variable.getName()+" -> "+variable.getName()+")";
|
||||
}
|
||||
|
||||
static boolean isExpressionDependsOnUpdatedCollections(PsiExpression condition,
|
||||
PsiExpression qualifierExpression) {
|
||||
final PsiElement collection = qualifierExpression instanceof PsiReferenceExpression
|
||||
? ((PsiReferenceExpression)qualifierExpression).resolve()
|
||||
: null;
|
||||
if (collection != null) {
|
||||
return collection instanceof PsiVariable && VariableAccessUtils.variableIsUsed((PsiVariable)collection, condition);
|
||||
}
|
||||
|
||||
final boolean[] dependsOnCollection = {false};
|
||||
condition.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
|
||||
super.visitMethodCallExpression(expression);
|
||||
final PsiExpression callQualifier = expression.getMethodExpression().getQualifierExpression();
|
||||
if (callQualifier == null ||
|
||||
callQualifier instanceof PsiThisExpression && ((PsiThisExpression)callQualifier).getQualifier() == null ||
|
||||
callQualifier instanceof PsiSuperExpression && ((PsiSuperExpression)callQualifier).getQualifier() == null) {
|
||||
dependsOnCollection[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitThisExpression(PsiThisExpression expression) {
|
||||
super.visitThisExpression(expression);
|
||||
if (expression.getQualifier() == null && expression.getParent() instanceof PsiExpressionList) {
|
||||
dependsOnCollection[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClass(PsiClass aClass) {}
|
||||
|
||||
@Override
|
||||
public void visitLambdaExpression(PsiLambdaExpression expression) {}
|
||||
});
|
||||
|
||||
return dependsOnCollection[0];
|
||||
}
|
||||
|
||||
private class StreamApiMigrationVisitor extends JavaElementVisitor {
|
||||
private final ProblemsHolder myHolder;
|
||||
private final boolean myIsOnTheFly;
|
||||
@@ -621,22 +549,27 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
if (isCountOperation(nonFinalVariables, tb)) {
|
||||
return new CountMigration();
|
||||
}
|
||||
if (isCollectCall(tb) && nonFinalVariables.isEmpty()) {
|
||||
return findCollectMigration(loop, tb);
|
||||
if (nonFinalVariables.isEmpty()) {
|
||||
CollectMigration.CollectTerminal terminal = CollectMigration.extractCollectTerminal(tb);
|
||||
if(terminal != null && getInitializerUsageStatus(terminal.getTargetVariable(), loop) != UNKNOWN) {
|
||||
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;
|
||||
if(!REPLACE_TRIVIAL_FOREACH && !tb.hasOperations() && terminal.isTrivial()) return null;
|
||||
return new CollectMigration(terminal.getMethodName());
|
||||
}
|
||||
}
|
||||
if (tb.getCountExpression() != null || tb.isEmpty()) return null;
|
||||
if (getAccumulatedVariable(tb, nonFinalVariables) != null) {
|
||||
return new SumMigration();
|
||||
}
|
||||
if (isCollectMapCall(tb) && nonFinalVariables.isEmpty() && (REPLACE_TRIVIAL_FOREACH || tb.hasOperations())) {
|
||||
return new CollectMigration("collect");
|
||||
}
|
||||
Collection<PsiStatement> exitPoints = tb.findExitPoints(controlFlow);
|
||||
if (exitPoints == null) return null;
|
||||
// do not replace for(T e : arr) {} with Arrays.stream(arr).forEach(e -> {}) even if flag is set
|
||||
if (SUGGEST_FOREACH && exitPoints.isEmpty() && nonFinalVariables.isEmpty() &&
|
||||
(tb.hasOperations() ||
|
||||
(!(tb.getLastOperation() instanceof ArrayStream) && (REPLACE_TRIVIAL_FOREACH || !isTrivial(body, loop))))) {
|
||||
if (SUGGEST_FOREACH && exitPoints.isEmpty() && nonFinalVariables.isEmpty()) {
|
||||
boolean nonTrivial = tb.hasOperations() || ForEachMigration.tryExtractMapExpression(tb) != null || !isTrivial(tb);
|
||||
// do not replace for(T e : arr) {} with Arrays.stream(arr).forEach(e -> {}) even if REPLACE_TRIVIAL_FOREACH is set
|
||||
if (!nonTrivial && (!REPLACE_TRIVIAL_FOREACH || tb.getLastOperation() instanceof ArrayStream)) return null;
|
||||
return new ForEachMigration("forEach");
|
||||
}
|
||||
if (!tb.hasOperations() && !REPLACE_TRIVIAL_FOREACH) return null;
|
||||
@@ -672,41 +605,18 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
if (nonFinalVariables.size() == 1) {
|
||||
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(statement);
|
||||
if(assignment == null) return null;
|
||||
PsiExpression lValue = assignment.getLExpression();
|
||||
if (!(lValue instanceof PsiReferenceExpression)) return null;
|
||||
PsiElement var = ((PsiReferenceExpression)lValue).resolve();
|
||||
if(!(var instanceof PsiVariable) || !nonFinalVariables.contains(var)) 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((PsiVariable)var, rValue)) return null;
|
||||
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();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BaseStreamApiMigration findCollectMigration(PsiLoopStatement statement, TerminalBlock tb) {
|
||||
boolean addAll = statement instanceof PsiForeachStatement && !tb.hasOperations() && isAddAllCall(tb);
|
||||
// Don't suggest to convert the loop which can be trivially replaced via addAll:
|
||||
// this is covered by UseBulkOperationInspection and ManualArrayToCollectionCopyInspection
|
||||
if(addAll) return null;
|
||||
PsiMethodCallExpression call = tb.getSingleMethodCall();
|
||||
if(call != null && call.getMethodExpression().getQualifierExpression() instanceof PsiMethodCallExpression) {
|
||||
call = (PsiMethodCallExpression)call.getMethodExpression().getQualifierExpression();
|
||||
}
|
||||
String methodName;
|
||||
if(canCollect(statement, call)) {
|
||||
if(extractToArrayExpression(statement, call) != null)
|
||||
methodName = "toArray";
|
||||
else
|
||||
methodName = "collect";
|
||||
} else {
|
||||
if (!SUGGEST_FOREACH || tb.getCountExpression() != null) return null;
|
||||
methodName = "forEach";
|
||||
}
|
||||
return new CollectMigration(methodName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BaseStreamApiMigration findMigrationForReturn(PsiLoopStatement statement, TerminalBlock tb) {
|
||||
PsiReturnStatement returnStatement = (PsiReturnStatement)tb.getSingleStatement();
|
||||
@@ -776,75 +686,6 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static PsiMethodCallExpression extractToArrayExpression(PsiLoopStatement statement, PsiMethodCallExpression expression) {
|
||||
// return collection.toArray() or collection.toArray(new Type[0]) or collection.toArray(new Type[collection.size()]);
|
||||
PsiElement nextElement = PsiTreeUtil.skipSiblingsForward(statement, PsiComment.class, PsiWhiteSpace.class);
|
||||
PsiExpression toArrayCandidate;
|
||||
if (nextElement instanceof PsiReturnStatement) {
|
||||
toArrayCandidate = ((PsiReturnStatement)nextElement).getReturnValue();
|
||||
}
|
||||
else {
|
||||
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(nextElement);
|
||||
if (assignment != null) {
|
||||
toArrayCandidate = assignment.getRExpression();
|
||||
}
|
||||
else if (nextElement instanceof PsiDeclarationStatement) {
|
||||
PsiElement[] elements = ((PsiDeclarationStatement)nextElement).getDeclaredElements();
|
||||
if (elements.length == 1 && elements[0] instanceof PsiLocalVariable) {
|
||||
toArrayCandidate = ((PsiLocalVariable)elements[0]).getInitializer();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (!(toArrayCandidate instanceof PsiMethodCallExpression)) return null;
|
||||
PsiMethodCallExpression call = (PsiMethodCallExpression)toArrayCandidate;
|
||||
PsiReferenceExpression methodExpression = call.getMethodExpression();
|
||||
if (!"toArray".equals(methodExpression.getReferenceName())) return null;
|
||||
PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
|
||||
if (!(qualifierExpression instanceof PsiReferenceExpression)) return null;
|
||||
PsiLocalVariable collectionVariable = extractCollectionVariable(expression.getMethodExpression().getQualifierExpression());
|
||||
if (collectionVariable == null || !((PsiReferenceExpression)qualifierExpression).isReferenceTo(collectionVariable)) return null;
|
||||
PsiExpression initializer = collectionVariable.getInitializer();
|
||||
if (initializer == null) return null;
|
||||
PsiType type = initializer.getType();
|
||||
if (!(type instanceof PsiClassType) || !COLLECTION_TO_ARRAY.containsKey(((PsiClassType)type).rawType().getCanonicalText())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!(nextElement instanceof PsiReturnStatement) && !ReferencesSearch.search(collectionVariable)
|
||||
.forEach(ref ->
|
||||
ref.getElement() == collectionVariable || PsiTreeUtil.isAncestor(statement, ref.getElement(), false) ||
|
||||
PsiTreeUtil.isAncestor(toArrayCandidate, ref.getElement(), false)
|
||||
)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
if (args.length == 0) return call;
|
||||
if (args.length != 1 || !(args[0] instanceof PsiNewExpression)) return null;
|
||||
PsiNewExpression newArray = (PsiNewExpression)args[0];
|
||||
PsiExpression[] dimensions = newArray.getArrayDimensions();
|
||||
if (dimensions.length != 1) return null;
|
||||
if (ExpressionUtils.isLiteral(dimensions[0], 0)) return call;
|
||||
if (!(dimensions[0] instanceof PsiMethodCallExpression)) return null;
|
||||
PsiMethodCallExpression maybeSizeCall = (PsiMethodCallExpression)dimensions[0];
|
||||
if (maybeSizeCall.getArgumentList().getExpressions().length != 0) return null;
|
||||
PsiReferenceExpression maybeSizeExpression = maybeSizeCall.getMethodExpression();
|
||||
PsiExpression sizeQualifier = maybeSizeExpression.getQualifierExpression();
|
||||
if (sizeQualifier != null &&
|
||||
!("size".equals(maybeSizeExpression.getReferenceName()) &&
|
||||
PsiEquivalenceUtil.areElementsEquivalent(qualifierExpression, sizeQualifier))) {
|
||||
return null;
|
||||
}
|
||||
return call;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intermediate stream operation representation
|
||||
*/
|
||||
@@ -1137,28 +978,24 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
@Nullable
|
||||
public static BufferedReaderLines from(PsiWhileStatement whileLoop) {
|
||||
// while ((line = br.readLine()) != null)
|
||||
PsiExpression condition = PsiUtil.skipParenthesizedExprDown(whileLoop.getCondition());
|
||||
if(!(condition instanceof PsiBinaryExpression)) return null;
|
||||
PsiBinaryExpression binOp = (PsiBinaryExpression)condition;
|
||||
PsiBinaryExpression binOp = tryCast(PsiUtil.skipParenthesizedExprDown(whileLoop.getCondition()), PsiBinaryExpression.class);
|
||||
if(binOp == null) return null;
|
||||
if(!JavaTokenType.NE.equals(binOp.getOperationTokenType())) return null;
|
||||
PsiExpression operand = ExpressionUtils.getValueComparedWithNull(binOp);
|
||||
if(operand == null) return null;
|
||||
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(PsiUtil.skipParenthesizedExprDown(operand));
|
||||
if(assignment == null) return null;
|
||||
PsiExpression lValue = assignment.getLExpression();
|
||||
if(!(lValue instanceof PsiReferenceExpression)) return null;
|
||||
PsiElement element = ((PsiReferenceExpression)lValue).resolve();
|
||||
if(!(element instanceof PsiLocalVariable)) return null;
|
||||
PsiLocalVariable var = (PsiLocalVariable)element;
|
||||
PsiReferenceExpression lValue = tryCast(assignment.getLExpression(), PsiReferenceExpression.class);
|
||||
if(lValue == null) return null;
|
||||
PsiLocalVariable var = tryCast(lValue.resolve(), PsiLocalVariable.class);
|
||||
if(var == null) return null;
|
||||
if(!ReferencesSearch.search(var).forEach(ref -> {
|
||||
return PsiTreeUtil.isAncestor(whileLoop, ref.getElement(), true);
|
||||
})) {
|
||||
return null;
|
||||
}
|
||||
PsiExpression rValue = PsiUtil.skipParenthesizedExprDown(assignment.getRExpression());
|
||||
if(!(rValue instanceof PsiMethodCallExpression)) return null;
|
||||
PsiMethodCallExpression call = (PsiMethodCallExpression)rValue;
|
||||
if(call.getArgumentList().getExpressions().length != 0) return null;
|
||||
PsiMethodCallExpression call = tryCast(PsiUtil.skipParenthesizedExprDown(assignment.getRExpression()), PsiMethodCallExpression.class);
|
||||
if (call == null || call.getArgumentList().getExpressions().length != 0) return null;
|
||||
if(!"readLine".equals(call.getMethodExpression().getReferenceName())) return null;
|
||||
PsiExpression readerExpression = call.getMethodExpression().getQualifierExpression();
|
||||
if(readerExpression == null) return null;
|
||||
@@ -1185,17 +1022,14 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
PsiExpression iteratedValue = statement.getIteratedValue();
|
||||
if (iteratedValue == null) return null;
|
||||
|
||||
PsiType iteratedValueType = iteratedValue.getType();
|
||||
PsiArrayType iteratedValueType = tryCast(iteratedValue.getType(), PsiArrayType.class);
|
||||
PsiParameter parameter = statement.getIterationParameter();
|
||||
|
||||
if (!(iteratedValueType instanceof PsiArrayType) ||
|
||||
!isSupported(((PsiArrayType)iteratedValueType).getComponentType()) ||
|
||||
((parameter.getType() instanceof PsiPrimitiveType) &&
|
||||
!parameter.getType().equals(((PsiArrayType)iteratedValueType).getComponentType()))) {
|
||||
return null;
|
||||
if (iteratedValueType != null && isSupported(iteratedValueType.getComponentType()) &&
|
||||
(!(parameter.getType() instanceof PsiPrimitiveType) || parameter.getType().equals(iteratedValueType.getComponentType()))) {
|
||||
return new ArrayStream(statement, parameter, iteratedValue);
|
||||
}
|
||||
|
||||
return new ArrayStream(statement, parameter, iteratedValue);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1212,8 +1046,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
|
||||
@Contract("null, _ -> false")
|
||||
static boolean isRawSubstitution(PsiType iteratedValueType, PsiClass collectionClass) {
|
||||
return iteratedValueType instanceof PsiClassType && PsiUtil
|
||||
.isRawSubstitutor(collectionClass, TypeConversionUtil.getSuperClassSubstitutor(collectionClass, (PsiClassType)iteratedValueType));
|
||||
return iteratedValueType instanceof PsiClassType &&
|
||||
PsiUtil.isRawSubstitutor(collectionClass,
|
||||
TypeConversionUtil.getSuperClassSubstitutor(collectionClass, (PsiClassType)iteratedValueType));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -1275,12 +1110,10 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
@Nullable
|
||||
public static CountingLoop from(PsiForStatement forStatement) {
|
||||
// check that initialization is for(int/long i = <initial_value>;...;...)
|
||||
if(!(forStatement.getInitialization() instanceof PsiDeclarationStatement)) return null;
|
||||
PsiDeclarationStatement initialization = (PsiDeclarationStatement)forStatement.getInitialization();
|
||||
if(initialization.getDeclaredElements().length != 1) return null;
|
||||
PsiElement declaration = initialization.getDeclaredElements()[0];
|
||||
if(!(declaration instanceof PsiLocalVariable)) return null;
|
||||
PsiLocalVariable counter = (PsiLocalVariable)declaration;
|
||||
PsiDeclarationStatement initialization = tryCast(forStatement.getInitialization(), PsiDeclarationStatement.class);
|
||||
if (initialization == null || initialization.getDeclaredElements().length != 1) return null;
|
||||
PsiLocalVariable counter = tryCast(initialization.getDeclaredElements()[0], PsiLocalVariable.class);
|
||||
if(counter == null) return null;
|
||||
if(!counter.getType().equals(PsiType.INT) && !counter.getType().equals(PsiType.LONG)) return null;
|
||||
|
||||
PsiExpression initializer = counter.getInitializer();
|
||||
@@ -1290,8 +1123,8 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
if(!VariableAccessUtils.variableIsIncremented(counter, forStatement.getUpdate())) return null;
|
||||
|
||||
// check that condition is like for(...;i<bound;...) or for(...;i<=bound;...)
|
||||
if(!(forStatement.getCondition() instanceof PsiBinaryExpression)) return null;
|
||||
PsiBinaryExpression condition = (PsiBinaryExpression)forStatement.getCondition();
|
||||
PsiBinaryExpression condition = tryCast(forStatement.getCondition(), PsiBinaryExpression.class);
|
||||
if(condition == null) return null;
|
||||
IElementType type = condition.getOperationTokenType();
|
||||
boolean closed = false;
|
||||
PsiExpression bound;
|
||||
@@ -1311,9 +1144,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
bound = condition.getLOperand();
|
||||
ref = condition.getROperand();
|
||||
} else return null;
|
||||
if(bound == null || !(ref instanceof PsiReferenceExpression) || !((PsiReferenceExpression)ref).isReferenceTo(counter)) return null;
|
||||
if(bound == null || !ExpressionUtils.isReferenceTo(ref, counter)) return null;
|
||||
if(!TypeConversionUtil.areTypesAssignmentCompatible(counter.getType(), bound)) return null;
|
||||
return new CountingLoop(forStatement, counter, initializer, bound, closed);
|
||||
return new CountingLoop(forStatement, counter, initializer, bound, closed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.IntArrayList;
|
||||
import com.siyeh.ig.psiutils.*;
|
||||
import one.util.streamex.StreamEx;
|
||||
@@ -36,6 +35,7 @@ import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.*;
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
/**
|
||||
* This immutable class represents the code which should be performed
|
||||
@@ -97,7 +97,7 @@ class TerminalBlock {
|
||||
PsiStatement statement = getSingleStatement();
|
||||
if(statement instanceof PsiExpressionStatement) {
|
||||
PsiExpression expression = ((PsiExpressionStatement)statement).getExpression();
|
||||
return ObjectUtils.tryCast(expression, wantedType);
|
||||
return tryCast(expression, wantedType);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -195,16 +195,13 @@ class TerminalBlock {
|
||||
PsiDeclarationStatement decl = (PsiDeclarationStatement)first;
|
||||
PsiElement[] elements = decl.getDeclaredElements();
|
||||
if(elements.length == 1) {
|
||||
PsiElement element = elements[0];
|
||||
if(element instanceof PsiLocalVariable) {
|
||||
PsiLocalVariable declaredVar = (PsiLocalVariable)element;
|
||||
if (isSupported(declaredVar.getType())) {
|
||||
PsiExpression initializer = declaredVar.getInitializer();
|
||||
PsiStatement[] leftOver = Arrays.copyOfRange(myStatements, 1, myStatements.length);
|
||||
if (initializer != null && ReferencesSearch.search(myVariable, new LocalSearchScope(leftOver)).findFirst() == null) {
|
||||
MapOp op = new MapOp(initializer, myVariable, declaredVar.getType());
|
||||
return new TerminalBlock(this, op, declaredVar, leftOver);
|
||||
}
|
||||
PsiLocalVariable declaredVar = tryCast(elements[0], PsiLocalVariable.class);
|
||||
if (declaredVar != null && isSupported(declaredVar.getType())) {
|
||||
PsiExpression initializer = declaredVar.getInitializer();
|
||||
PsiStatement[] leftOver = Arrays.copyOfRange(myStatements, 1, myStatements.length);
|
||||
if (initializer != null && ReferencesSearch.search(myVariable, new LocalSearchScope(leftOver)).findFirst() == null) {
|
||||
MapOp op = new MapOp(initializer, myVariable, declaredVar.getType());
|
||||
return new TerminalBlock(this, op, declaredVar, leftOver);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -244,11 +241,9 @@ class TerminalBlock {
|
||||
}
|
||||
if (tb == null || !ControlFlowUtils.statementBreaksLoop(tb.getSingleStatement(), getMainLoop())) return this;
|
||||
FilterOp filter = tb.getLastOperation(FilterOp.class);
|
||||
if(filter == null) return this;
|
||||
PsiExpression condition = PsiUtil.skipParenthesizedExprDown(filter.getExpression());
|
||||
if(!(condition instanceof PsiBinaryExpression)) return this;
|
||||
PsiBinaryExpression binOp = (PsiBinaryExpression)condition;
|
||||
if(!ComparisonUtils.isComparison(binOp)) return this;
|
||||
if (filter == null) return this;
|
||||
PsiBinaryExpression binOp = tryCast(PsiUtil.skipParenthesizedExprDown(filter.getExpression()), PsiBinaryExpression.class);
|
||||
if (binOp == null || !ComparisonUtils.isComparison(binOp)) return this;
|
||||
String comparison = filter.isNegated() ? ComparisonUtils.getNegatedComparison(binOp.getOperationTokenType())
|
||||
: binOp.getOperationSign().getText();
|
||||
boolean flipped = false;
|
||||
@@ -276,10 +271,8 @@ class TerminalBlock {
|
||||
PsiLocalVariable var = null;
|
||||
if (dedicatedCounter) {
|
||||
if (!(incrementedValue instanceof PsiReferenceExpression)) return this;
|
||||
PsiElement element = ((PsiReferenceExpression)incrementedValue).resolve();
|
||||
if (!(element instanceof PsiLocalVariable)) return this;
|
||||
var = (PsiLocalVariable)element;
|
||||
if (!ExpressionUtils.isZero(var.getInitializer()) || ReferencesSearch.search(var).findAll().size() != 1) return this;
|
||||
var = tryCast(((PsiReferenceExpression)incrementedValue).resolve(), PsiLocalVariable.class);
|
||||
if (var == null || !ExpressionUtils.isZero(var.getInitializer()) || ReferencesSearch.search(var).findAll().size() != 1) return this;
|
||||
}
|
||||
PsiExpression limit = flipped ? binOp.getLOperand() : binOp.getROperand();
|
||||
if(!ExpressionUtils.isSimpleExpression(limit) || VariableAccessUtils.variableIsUsed(myVariable, limit)) return this;
|
||||
@@ -308,11 +301,19 @@ class TerminalBlock {
|
||||
if (!isCallOf(call, CommonClassNames.JAVA_UTIL_COLLECTION, "add")) return null;
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
if (args.length != 1 || !ExpressionUtils.isReferenceTo(args[0], myVariable)) return null;
|
||||
PsiLocalVariable collectionVariable = extractCollectionVariable(call.getMethodExpression().getQualifierExpression());
|
||||
if (collectionVariable == null || getInitializerUsageStatus(collectionVariable, getMainLoop()) == InitializerUsageStatus.UNKNOWN) {
|
||||
PsiReferenceExpression qualifier = tryCast(call.getMethodExpression().getQualifierExpression(), PsiReferenceExpression.class);
|
||||
if (qualifier == null) return null;
|
||||
PsiLocalVariable var = tryCast(qualifier.resolve(), PsiLocalVariable.class);
|
||||
if (var == null) return null;
|
||||
PsiNewExpression initializer = tryCast(var.getInitializer(), PsiNewExpression.class);
|
||||
if (initializer == null) return null;
|
||||
PsiExpressionList argumentList = initializer.getArgumentList();
|
||||
if (argumentList == null ||
|
||||
argumentList.getExpressions().length != 0 ||
|
||||
getInitializerUsageStatus(var, getMainLoop()) == InitializerUsageStatus.UNKNOWN) {
|
||||
return null;
|
||||
}
|
||||
return collectionVariable;
|
||||
return var;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -367,7 +368,7 @@ class TerminalBlock {
|
||||
|
||||
@Nullable
|
||||
<T extends Operation> T getLastOperation(Class<T> clazz) {
|
||||
return ObjectUtils.tryCast(getLastOperation(), clazz);
|
||||
return tryCast(getLastOperation(), clazz);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -464,44 +465,4 @@ class TerminalBlock {
|
||||
boolean isReferencedInOperations(PsiVariable variable) {
|
||||
return intermediateAndSourceExpressions().anyMatch(expr -> VariableAccessUtils.variableIsUsed(variable, expr));
|
||||
}
|
||||
|
||||
private static boolean isExpressionDependsOnUpdatedCollections(PsiExpression condition,
|
||||
PsiExpression qualifierExpression) {
|
||||
final PsiElement collection = qualifierExpression instanceof PsiReferenceExpression
|
||||
? ((PsiReferenceExpression)qualifierExpression).resolve()
|
||||
: null;
|
||||
if (collection != null) {
|
||||
return collection instanceof PsiVariable && VariableAccessUtils.variableIsUsed((PsiVariable)collection, condition);
|
||||
}
|
||||
|
||||
final boolean[] dependsOnCollection = {false};
|
||||
condition.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
|
||||
super.visitMethodCallExpression(expression);
|
||||
final PsiExpression callQualifier = expression.getMethodExpression().getQualifierExpression();
|
||||
if (callQualifier == null ||
|
||||
callQualifier instanceof PsiThisExpression && ((PsiThisExpression)callQualifier).getQualifier() == null ||
|
||||
callQualifier instanceof PsiSuperExpression && ((PsiSuperExpression)callQualifier).getQualifier() == null) {
|
||||
dependsOnCollection[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitThisExpression(PsiThisExpression expression) {
|
||||
super.visitThisExpression(expression);
|
||||
if (expression.getQualifier() == null && expression.getParent() instanceof PsiExpressionList) {
|
||||
dependsOnCollection[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClass(PsiClass aClass) {}
|
||||
|
||||
@Override
|
||||
public void visitLambdaExpression(PsiLambdaExpression expression) {}
|
||||
});
|
||||
|
||||
return dependsOnCollection[0];
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -8,8 +8,8 @@ public class Collect {
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> names = new HashSet<>();
|
||||
final Set<String> names = new HashSet<>();
|
||||
void collectNames(List<Person> persons){
|
||||
persons.stream().map(Person::getName).forEach(s -> names.add(s));
|
||||
persons.stream().map(Person::getName).forEach(names::add);
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Replace with toArray" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Test {
|
||||
String[] test(List<String> list) {
|
||||
return list.stream().filter(Objects::nonNull).sorted(String.CASE_INSENSITIVE_ORDER).toArray(String[]::new);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -8,7 +8,7 @@ public class Collect {
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> names = new HashSet<>();
|
||||
final Set<String> names = new HashSet<>();
|
||||
void collectNames(List<Person> persons){
|
||||
for (Person person : pers<caret>ons) {
|
||||
names.add(person.getName());
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import java.util.*;
|
||||
class A {
|
||||
void fun(List<String>... lists) {
|
||||
for (List<String> list : li<caret>sts) {
|
||||
System.out.println(list);
|
||||
list.add("");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// "Replace with toArray" "true"
|
||||
import java.util.*;
|
||||
|
||||
public class Test {
|
||||
String[] test(List<String> list) {
|
||||
List<String> result = new LinkedList<>();
|
||||
for(String str : li<caret>st) {
|
||||
if(str != null) {
|
||||
result.add(str);
|
||||
}
|
||||
}
|
||||
result.sort(String.CASE_INSENSITIVE_ORDER);
|
||||
return result.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user