mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
StreamToLoopInspection fixes: toArray intermediate list type fixed; keywords are filtered out from possible var names; context added to expression passed to BoolUtils; supported inside method calls
This commit is contained in:
@@ -20,8 +20,10 @@ import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.lang.java.lexer.JavaLexer;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
@@ -87,7 +89,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
private static boolean isSupportedCodeLocation(PsiMethodCallExpression call) {
|
||||
PsiElement cur = call;
|
||||
PsiElement parent = cur.getParent();
|
||||
while(parent instanceof PsiExpression) {
|
||||
while(parent instanceof PsiExpression || parent instanceof PsiExpressionList) {
|
||||
// TODO: support in single expression lambdas
|
||||
if(parent instanceof PsiLambdaExpression) {
|
||||
return false;
|
||||
@@ -133,9 +135,10 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
if(InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM)) {
|
||||
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
|
||||
if(qualifier != null) {
|
||||
Operation op = Operation.createIntermediate(name, args, outVar, getStreamElementType(qualifier.getType()));
|
||||
PsiType elementType = getStreamElementType(qualifier.getType());
|
||||
Operation op = Operation.createIntermediate(name, args, outVar, elementType);
|
||||
if (op != null) return op;
|
||||
op = TerminalOperation.createTerminal(name, args, callType, className, call.getParent() instanceof PsiExpressionStatement);
|
||||
op = TerminalOperation.createTerminal(name, args, elementType, callType, call.getParent() instanceof PsiExpressionStatement);
|
||||
if (op != null) return op;
|
||||
}
|
||||
}
|
||||
@@ -232,11 +235,12 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
String text = terminalCall.getText();
|
||||
if(temporaryStreamPlaceholder.isPhysical()) {
|
||||
// Just in case if something went wrong: at least try to restore the original stream code
|
||||
temporaryStreamPlaceholder.replace(factory.createExpressionFromText(terminalCall.getText(), temporaryStreamPlaceholder));
|
||||
temporaryStreamPlaceholder.replace(factory.createExpressionFromText(text, temporaryStreamPlaceholder));
|
||||
}
|
||||
throw ex;
|
||||
throw new RuntimeException("Error converting Stream to loop: "+text, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,9 +384,9 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
}
|
||||
|
||||
private boolean isUsed(String varName) {
|
||||
if(myUsedNames.contains(varName)) return true;
|
||||
// TODO: cleaner solution
|
||||
return !varName.equals(JavaCodeStyleManager.getInstance(myStatement.getProject()).suggestUniqueVariableName(varName, myStatement, true));
|
||||
return myUsedNames.contains(varName) || JavaLexer.isKeyword(varName, LanguageLevel.HIGHEST) ||
|
||||
// TODO: cleaner solution
|
||||
!varName.equals(JavaCodeStyleManager.getInstance(myStatement.getProject()).suggestUniqueVariableName(varName, myStatement, true));
|
||||
}
|
||||
|
||||
public String declare(String desiredName, String type, String initializer) {
|
||||
|
||||
@@ -63,7 +63,7 @@ abstract class TerminalOperation extends Operation {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static TerminalOperation createTerminal(String name, PsiExpression[] args, PsiType callType, String className, boolean isVoid) {
|
||||
static TerminalOperation createTerminal(String name, PsiExpression[] args, PsiType elementType, PsiType resultType, boolean isVoid) {
|
||||
if(isVoid) {
|
||||
if ((name.equals("forEach") || name.equals("forEachOrdered")) && args.length == 1) {
|
||||
FunctionHelper fn = FunctionHelper.create(args[0], 1, true);
|
||||
@@ -75,23 +75,22 @@ abstract class TerminalOperation extends Operation {
|
||||
return new AccumulatedTerminalOperation("count", "long", "0", "{acc}++;");
|
||||
}
|
||||
if(name.equals("sum") && args.length == 0) {
|
||||
return new AccumulatedTerminalOperation("sum", callType.getCanonicalText(), "0", "{acc}+={item};");
|
||||
return new AccumulatedTerminalOperation("sum", resultType.getCanonicalText(), "0", "{acc}+={item};");
|
||||
}
|
||||
if(name.equals("average") && args.length == 0) {
|
||||
if(className.equals(CommonClassNames.JAVA_UTIL_STREAM_DOUBLE_STREAM)) {
|
||||
if(elementType.equals(PsiType.DOUBLE)) {
|
||||
return new AverageTerminalOperation(true);
|
||||
}
|
||||
else if(className.equals(CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM) ||
|
||||
className.equals(CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM)) {
|
||||
else if(elementType.equals(PsiType.INT) || elementType.equals(PsiType.LONG)) {
|
||||
return new AverageTerminalOperation(false);
|
||||
}
|
||||
}
|
||||
if(name.equals("summaryStatistics") && args.length == 0) {
|
||||
return new AccumulatedTerminalOperation("stat", callType.getCanonicalText(), "new " + callType.getCanonicalText() + "()",
|
||||
return new AccumulatedTerminalOperation("stat", resultType.getCanonicalText(), "new " + resultType.getCanonicalText() + "()",
|
||||
"{acc}.accept({item});");
|
||||
}
|
||||
if((name.equals("findFirst") || name.equals("findAny")) && args.length == 0) {
|
||||
return new FindTerminalOperation(callType.getCanonicalText());
|
||||
return new FindTerminalOperation(resultType.getCanonicalText());
|
||||
}
|
||||
if((name.equals("anyMatch") || name.equals("allMatch") || name.equals("noneMatch")) && args.length == 1) {
|
||||
FunctionHelper fn = FunctionHelper.create(args[0], 1, true);
|
||||
@@ -101,11 +100,11 @@ abstract class TerminalOperation extends Operation {
|
||||
if(args.length == 2 || args.length == 3) {
|
||||
FunctionHelper fn = FunctionHelper.create(args[1], 2, true);
|
||||
if(fn != null) {
|
||||
return new ReduceTerminalOperation(args[0], fn, callType.getCanonicalText());
|
||||
return new ReduceTerminalOperation(args[0], fn, resultType.getCanonicalText());
|
||||
}
|
||||
}
|
||||
if(args.length == 1) {
|
||||
PsiType optionalElementType = getOptionalElementType(callType);
|
||||
PsiType optionalElementType = getOptionalElementType(resultType);
|
||||
FunctionHelper fn = FunctionHelper.create(args[0], 2, true);
|
||||
if(fn != null && optionalElementType != null) {
|
||||
return new ReduceToOptionalTerminalOperation(fn, optionalElementType.getCanonicalText());
|
||||
@@ -113,8 +112,8 @@ abstract class TerminalOperation extends Operation {
|
||||
}
|
||||
}
|
||||
if(name.equals("toArray") && args.length < 2) {
|
||||
if(!(callType instanceof PsiArrayType)) return null;
|
||||
PsiType componentType = ((PsiArrayType)callType).getComponentType();
|
||||
if(!(resultType instanceof PsiArrayType)) return null;
|
||||
PsiType componentType = ((PsiArrayType)resultType).getComponentType();
|
||||
if (componentType instanceof PsiPrimitiveType) {
|
||||
if(args.length == 0) return new ToPrimitiveArrayTerminalOperation(componentType.getCanonicalText());
|
||||
}
|
||||
@@ -130,7 +129,7 @@ abstract class TerminalOperation extends Operation {
|
||||
if(!(type instanceof PsiArrayType)) return null;
|
||||
arr = "new "+type.getCanonicalText().replaceFirst("\\[]", "[0]");
|
||||
}
|
||||
return new AccumulatedTerminalOperation("list", CommonClassNames.JAVA_UTIL_LIST + "<" + componentType.getCanonicalText() + ">",
|
||||
return new AccumulatedTerminalOperation("list", CommonClassNames.JAVA_UTIL_LIST + "<" + elementType.getCanonicalText() + ">",
|
||||
"new "+ CommonClassNames.JAVA_UTIL_ARRAY_LIST+"<>()", "{acc}.add({item});",
|
||||
"{acc}.toArray("+arr+")");
|
||||
}
|
||||
@@ -144,25 +143,25 @@ abstract class TerminalOperation extends Operation {
|
||||
PsiClass collectorClass = collector.getContainingClass();
|
||||
if(collectorClass != null && CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS.equals(collectorClass.getQualifiedName())) {
|
||||
if(collector.getName().equals("toList") && collectorArgs.length == 0) {
|
||||
return AccumulatedTerminalOperation.toCollection(callType, CommonClassNames.JAVA_UTIL_ARRAY_LIST, "list");
|
||||
return AccumulatedTerminalOperation.toCollection(resultType, CommonClassNames.JAVA_UTIL_ARRAY_LIST, "list");
|
||||
}
|
||||
if(collector.getName().equals("toSet") && collectorArgs.length == 0) {
|
||||
return AccumulatedTerminalOperation.toCollection(callType, CommonClassNames.JAVA_UTIL_HASH_SET, "set");
|
||||
return AccumulatedTerminalOperation.toCollection(resultType, CommonClassNames.JAVA_UTIL_HASH_SET, "set");
|
||||
}
|
||||
if(collector.getName().equals("toCollection") && collectorArgs.length == 1) {
|
||||
FunctionHelper fn = FunctionHelper.create(collectorArgs[0], 0, true);
|
||||
if(fn != null) {
|
||||
return new ToCollectionTerminalOperation(fn, callType);
|
||||
return new ToCollectionTerminalOperation(fn, resultType);
|
||||
}
|
||||
}
|
||||
if(collector.getName().equals("reducing") && collectorArgs.length == 2) {
|
||||
FunctionHelper fn = FunctionHelper.create(collectorArgs[1], 2, true);
|
||||
if(fn != null) {
|
||||
return new ReduceTerminalOperation(collectorArgs[0], fn, callType.getCanonicalText());
|
||||
return new ReduceTerminalOperation(collectorArgs[0], fn, resultType.getCanonicalText());
|
||||
}
|
||||
}
|
||||
if(collector.getName().equals("reducing") && collectorArgs.length == 1) {
|
||||
PsiType optionalElementType = getOptionalElementType(callType);
|
||||
PsiType optionalElementType = getOptionalElementType(resultType);
|
||||
FunctionHelper fn = FunctionHelper.create(collectorArgs[0], 2, true);
|
||||
if(fn != null && optionalElementType != null) {
|
||||
return new ReduceToOptionalTerminalOperation(fn, optionalElementType.getCanonicalText());
|
||||
@@ -355,7 +354,14 @@ abstract class TerminalOperation extends Operation {
|
||||
@Override
|
||||
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
|
||||
myFn.transform(context, inVar.getName());
|
||||
String expression = myNegatePredicate ? BoolUtils.getNegatedExpressionText(myFn.getExpression()) : myFn.getText();
|
||||
String expression;
|
||||
if (myNegatePredicate) {
|
||||
PsiLambdaExpression lambda = (PsiLambdaExpression)context.createExpression("(" + inVar.getDeclaration() + ")->" + myFn.getText());
|
||||
expression = BoolUtils.getNegatedExpressionText((PsiExpression)lambda.getBody());
|
||||
}
|
||||
else {
|
||||
expression = myFn.getText();
|
||||
}
|
||||
return "if(" + expression + ") {\n" +
|
||||
context.assignAndBreak(myName, PsiType.BOOLEAN.getCanonicalText(), String.valueOf(!myDefaultValue), String.valueOf(myDefaultValue)) +
|
||||
"}\n";
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
private static boolean test(List<String> list) {
|
||||
for (String s : list) {
|
||||
if (s.trim() != s.toLowerCase()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(test(Arrays.asList("a", "b", "c")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class Main {
|
||||
private static test(List<String> packages) {
|
||||
Optional<String> found = Optional.empty();
|
||||
for (String s : packages) {
|
||||
if (s.startsWith("xyz")) {
|
||||
found = Optional.of(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found.filter(pkg -> pkg.endsWith("abc")).isPresent();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(test(Arrays.asList("xyzabc", "xyz123", "123abc", "123")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
public class Main {
|
||||
private static void test(List<String> test) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String s : test) {
|
||||
sb.append(s);
|
||||
}
|
||||
System.out.println("x"+ sb.toString() +"y");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(test(Arrays.asList("a", "b", "c")));
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import java.util.List;
|
||||
|
||||
public class Main {
|
||||
private static List<?>[] test(int[] numbers) {
|
||||
List<List<?>> list = new ArrayList<>();
|
||||
List<List<Integer>> list = new ArrayList<>();
|
||||
for (int number : numbers) {
|
||||
Integer n = number;
|
||||
List<Integer> integers = Collections.singletonList(n);
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.util.List;
|
||||
|
||||
public class Main {
|
||||
private static Object[] test(int[] numbers) {
|
||||
List<Object> list = new ArrayList<>();
|
||||
List<Integer> list = new ArrayList<>();
|
||||
for (int number : numbers) {
|
||||
Integer integer = number;
|
||||
list.add(integer);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Main {
|
||||
private static Number[] test(Object[] objects) {
|
||||
List<Object> list = new ArrayList<>();
|
||||
for (Object object : objects) {
|
||||
if (Number.class.isInstance(object)) {
|
||||
list.add(object);
|
||||
}
|
||||
}
|
||||
return list.toArray(new Number[0]);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(Arrays.asList(test(new Object[]{1, 2, 3, "string", 4})));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
private static boolean test(List<String> list) {
|
||||
return list.stream().al<caret>lMatch(s -> s.trim() == s.toLowerCase());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(test(Arrays.asList("a", "b", "c")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
private static test(List<String> packages) {
|
||||
return packages.stream().filter(pkg -> pkg.startsWith("xyz")).fin<caret>dAny().filter(pkg -> pkg.endsWith("abc")).isPresent();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(test(Arrays.asList("xyzabc", "xyz123", "123abc", "123")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
public class Main {
|
||||
private static void test(List<String> test) {
|
||||
System.out.println("x"+test.stream().c<caret>ollect(Collectors.joining())+"y");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(test(Arrays.asList("a", "b", "c")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Main {
|
||||
private static Number[] test(Object[] objects) {
|
||||
return Stream.of(objects).filter(Number.class::isInstance).toArr<caret>ay(Number[]::new);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(Arrays.asList(test(new Object[]{1, 2, 3, "string", 4})));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user