mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
StreamToLoopInspection: support Iterable.forEach()
This commit is contained in:
@@ -56,7 +56,7 @@ abstract class SourceOperation extends Operation {
|
||||
static SourceOperation createSource(PsiMethodCallExpression call, boolean supportUnknownSources) {
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
PsiType callType = call.getType();
|
||||
if(callType == null) return null;
|
||||
if(callType == null || PsiType.VOID.equals(callType)) return null;
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if(method == null) return null;
|
||||
String name = method.getName();
|
||||
|
||||
+67
-22
@@ -84,25 +84,25 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if(method == null) return;
|
||||
PsiClass aClass = method.getContainingClass();
|
||||
if(!InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM)) return;
|
||||
PsiMethodCallExpression currentCall = call;
|
||||
while(true) {
|
||||
Operation op = createOperationFromCall(StreamVariable.STUB, currentCall, SUPPORT_UNKNOWN_SOURCES);
|
||||
if(op == null) return;
|
||||
if(op instanceof SourceOperation) {
|
||||
TextRange range;
|
||||
if(isOnTheFly && InspectionProjectProfileManager.isInformationLevel(getShortName(), call)) {
|
||||
range = new TextRange(0, call.getTextLength());
|
||||
} else {
|
||||
range = nameElement.getTextRange().shiftRight(-call.getTextOffset());
|
||||
}
|
||||
holder.registerProblem(call, range, "Replace stream API chain with loop", new ReplaceStreamWithLoopFix());
|
||||
return;
|
||||
if (InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM)) {
|
||||
if (extractOperations(StreamVariable.STUB, call, SUPPORT_UNKNOWN_SOURCES) != null) {
|
||||
register(call, nameElement, "Replace Stream API chain with loop");
|
||||
}
|
||||
PsiExpression qualifier = currentCall.getMethodExpression().getQualifierExpression();
|
||||
if(!(qualifier instanceof PsiMethodCallExpression)) return;
|
||||
currentCall = (PsiMethodCallExpression)qualifier;
|
||||
}
|
||||
else if (extractIterableForEach(call) != null) {
|
||||
register(call, nameElement, "Replace 'forEach' call with loop");
|
||||
}
|
||||
}
|
||||
|
||||
private void register(PsiMethodCallExpression call, PsiElement nameElement, String message) {
|
||||
TextRange range;
|
||||
if (isOnTheFly && InspectionProjectProfileManager.isInformationLevel(getShortName(), call)) {
|
||||
range = new TextRange(0, call.getTextLength());
|
||||
}
|
||||
else {
|
||||
range = nameElement.getTextRange().shiftRight(-call.getTextOffset());
|
||||
}
|
||||
holder.registerProblem(call, range, message, new ReplaceStreamWithLoopFix(message));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -169,11 +169,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
}
|
||||
Operation op = Operation.createIntermediate(name, args, outVar, elementType, supportUnknownSources);
|
||||
if (op != null) return op;
|
||||
PsiElement parent = call.getParent();
|
||||
boolean isVoid = parent instanceof PsiExpressionStatement ||
|
||||
(parent instanceof PsiLambdaExpression &&
|
||||
PsiType.VOID.equals(LambdaUtil.getFunctionalInterfaceReturnType((PsiLambdaExpression)parent)));
|
||||
op = TerminalOperation.createTerminal(name, args, elementType, callType, isVoid);
|
||||
op = TerminalOperation.createTerminal(name, args, elementType, callType, isVoidContext(call.getParent()));
|
||||
if (op != null) return op;
|
||||
}
|
||||
return null;
|
||||
@@ -181,6 +177,39 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
return SourceOperation.createSource(call, supportUnknownSources);
|
||||
}
|
||||
|
||||
private static boolean isVoidContext(PsiElement element) {
|
||||
return element instanceof PsiExpressionStatement ||
|
||||
(element instanceof PsiLambdaExpression &&
|
||||
PsiType.VOID.equals(LambdaUtil.getFunctionalInterfaceReturnType((PsiLambdaExpression)element)));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static List<OperationRecord> extractIterableForEach(PsiMethodCallExpression terminalCall) {
|
||||
if (MethodCallUtils.isCallToMethod(terminalCall, CommonClassNames.JAVA_LANG_ITERABLE, PsiType.VOID, "forEach", new PsiType[1])
|
||||
&& isVoidContext(terminalCall.getParent())) {
|
||||
PsiExpression qualifier = terminalCall.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) return null;
|
||||
// Do not visit this path if some class implements both Iterable and Stream
|
||||
PsiType type = qualifier.getType();
|
||||
if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM)) return null;
|
||||
PsiExpression[] args = terminalCall.getArgumentList().getExpressions();
|
||||
if (args.length != 1) return null;
|
||||
FunctionHelper fn = FunctionHelper.create(args[0], 1);
|
||||
if (fn == null) return null;
|
||||
PsiType elementType = PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_LANG_ITERABLE, 0, false);
|
||||
if(elementType == null) return null;
|
||||
TerminalOperation terminal = new TerminalOperation.ForEachTerminalOperation(fn);
|
||||
SourceOperation source = new SourceOperation.ForEachSource(qualifier);
|
||||
OperationRecord terminalRecord = new OperationRecord();
|
||||
OperationRecord sourceRecord = new OperationRecord();
|
||||
terminalRecord.myOperation = terminal;
|
||||
sourceRecord.myOperation = source;
|
||||
sourceRecord.myOutVar = terminalRecord.myInVar = new StreamVariable(elementType.getCanonicalText());
|
||||
sourceRecord.myInVar = terminalRecord.myOutVar = StreamVariable.STUB;
|
||||
return Arrays.asList(sourceRecord, terminalRecord);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static List<OperationRecord> extractOperations(StreamVariable outVar,
|
||||
@@ -234,6 +263,19 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
}
|
||||
|
||||
static class ReplaceStreamWithLoopFix implements LocalQuickFix {
|
||||
private String myMessage;
|
||||
|
||||
public ReplaceStreamWithLoopFix(String message) {
|
||||
myMessage = message;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return myMessage;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -253,6 +295,9 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
PsiType resultType = terminalCall.getType();
|
||||
if (resultType == null) return;
|
||||
List<OperationRecord> operations = extractOperations(StreamVariable.STUB, terminalCall, true);
|
||||
if (operations == null) {
|
||||
operations = extractIterableForEach(terminalCall);
|
||||
}
|
||||
TerminalOperation terminal = getTerminal(operations);
|
||||
if (terminal == null) return;
|
||||
allOperations(operations).forEach(or -> or.myOperation.suggestNames(or.myInVar, or.myOutVar));
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// "Fix all 'Stream API call chain can be replaced with loop' problems in file" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Main {
|
||||
public void testCast(Object obj, List<Object> list) {
|
||||
for (Number n : ((Iterable<Number>) obj)) {
|
||||
list.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<String> list = Arrays.asList("a", "b");
|
||||
for (String s : list) {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// "Fix all 'Stream API call chain can be replaced with loop' problems in file" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Main {
|
||||
public void testCast(Object obj, List<Object> list) {
|
||||
((Iterable<Number>)obj).forE<caret>ach(n -> list.add(n));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<String> list = Arrays.asList("a", "b");
|
||||
list.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user