mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
IDEA-167583 Uncompilable code after conversion Stream -> Loop with lambda
This commit is contained in:
@@ -106,14 +106,14 @@ abstract class FunctionHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
void suggestVariableName(StreamVariable var, int index) {
|
||||
void preprocessVariable(StreamToLoopReplacementContext context, StreamVariable var, int index) {
|
||||
String name = getParameterName(index);
|
||||
if (name != null) {
|
||||
var.addBestNameCandidate(name);
|
||||
}
|
||||
}
|
||||
|
||||
void suggestOutputNames(StreamVariable var) {}
|
||||
void suggestOutputNames(StreamToLoopReplacementContext context, StreamVariable var) {}
|
||||
|
||||
List<String> suggestFinalOutputNames(StreamToLoopReplacementContext context, String desiredName, String worstCaseName) {
|
||||
List<String> candidates = Arrays.asList(JavaCodeStyleManager.getInstance(context.getProject())
|
||||
@@ -433,11 +433,10 @@ abstract class FunctionHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
void suggestOutputNames(StreamVariable var) {
|
||||
void suggestOutputNames(StreamToLoopReplacementContext context, StreamVariable var) {
|
||||
// myMethodRef is physical at this point
|
||||
Project project = myMethodRef.getProject();
|
||||
PsiTypeCastExpression castExpr = (PsiTypeCastExpression)JavaPsiFacade.getElementFactory(project)
|
||||
.createExpressionFromText("(" + myType + ")" + myMethodRef.getText(), myMethodRef);
|
||||
PsiTypeCastExpression castExpr = (PsiTypeCastExpression)context.createExpression("(" + myType + ")" + myMethodRef.getText());
|
||||
PsiMethodReferenceExpression methodRef = (PsiMethodReferenceExpression)castExpr.getOperand();
|
||||
PsiLambdaExpression lambda = LambdaRefactoringUtil.convertMethodReferenceToLambda(methodRef, true, true);
|
||||
if(lambda != null) {
|
||||
@@ -632,9 +631,27 @@ abstract class FunctionHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
void suggestOutputNames(StreamVariable var) {
|
||||
void preprocessVariable(StreamToLoopReplacementContext context, StreamVariable var, int index) {
|
||||
super.preprocessVariable(context, var, index);
|
||||
boolean hasClassOrLambda =
|
||||
StreamEx.ofTree(myBody, e -> StreamEx.of(e.getChildren())).anyMatch(e -> e instanceof PsiLambdaExpression || e instanceof PsiClass);
|
||||
if (hasClassOrLambda) {
|
||||
PsiLambdaExpression lambda = (PsiLambdaExpression)context.createExpression(getParameterName(index) + "->" + myBody.getText());
|
||||
PsiParameter parameter = lambda.getParameterList().getParameters()[0];
|
||||
PsiElement body = lambda.getBody();
|
||||
LOG.assertTrue(body != null);
|
||||
boolean mayBeNotFinal = ReferencesSearch.search(parameter, new LocalSearchScope(body))
|
||||
.forEach(e -> PsiTreeUtil.getParentOfType(e.getElement(), PsiLambdaExpression.class, PsiClass.class) == lambda);
|
||||
if (!mayBeNotFinal) {
|
||||
var.markFinal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void suggestOutputNames(StreamToLoopReplacementContext context, StreamVariable var) {
|
||||
Project project = myBody.getProject();
|
||||
PsiExpression expr = JavaPsiFacade.getElementFactory(project).createExpressionFromText("(" + var.getType() + ")" + getText(), myBody);
|
||||
PsiExpression expr = context.createExpression("(" + var.getType() + ")" + getText());
|
||||
suggestFromExpression(var, project, expr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ abstract class Operation {
|
||||
|
||||
public void registerReusedElements(Consumer<PsiElement> consumer) {}
|
||||
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {}
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {}
|
||||
|
||||
@Nullable
|
||||
static Operation createIntermediate(@NotNull String name, @NotNull PsiExpression[] args,
|
||||
@@ -133,8 +133,8 @@ abstract class Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
myFn.suggestVariableName(inVar, 0);
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
myFn.preprocessVariable(context, inVar, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,9 +193,9 @@ abstract class Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
super.suggestNames(inVar, outVar);
|
||||
myFn.suggestOutputNames(outVar);
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
super.preprocessVariables(context, inVar, outVar);
|
||||
myFn.suggestOutputNames(context, outVar);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -253,8 +253,11 @@ abstract class Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
myFn.suggestVariableName(inVar, 0);
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
String name = myFn.getParameterName(0);
|
||||
if (name != null) {
|
||||
inVar.addBestNameCandidate(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -136,8 +136,8 @@ abstract class SourceOperation extends Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
if(myQualifier instanceof PsiReferenceExpression) {
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
if (myQualifier instanceof PsiReferenceExpression) {
|
||||
String name = ((PsiReferenceExpression)myQualifier).getReferenceName();
|
||||
if(name != null) {
|
||||
String singularName = StringUtil.unpluralize(name);
|
||||
@@ -264,8 +264,8 @@ abstract class SourceOperation extends Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
myFn.suggestVariableName(outVar, 0);
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
myFn.preprocessVariable(context, outVar, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -306,10 +306,17 @@ abstract class SourceOperation extends Operation {
|
||||
if(!ExpressionUtils.isSimpleExpression(context.createExpression(bound))) {
|
||||
bound = context.declare("bound", outVar.getType(), bound);
|
||||
}
|
||||
String loopVar = outVar.getName();
|
||||
String reassign = "";
|
||||
if (outVar.isFinal()) {
|
||||
loopVar = context.registerVarName(Arrays.asList("i", "j", "idx"));
|
||||
reassign = outVar.getDeclaration(loopVar);
|
||||
}
|
||||
return context.getLoopLabel() +
|
||||
"for(" + outVar.getDeclaration() + " = " + myOrigin.getText() + ";" +
|
||||
outVar + (myInclusive ? "<=" : "<") + bound + ";" +
|
||||
outVar + "++) {\n" +
|
||||
"for(" + outVar.getType() + " " + loopVar + " = " + myOrigin.getText() + ";" +
|
||||
loopVar + (myInclusive ? "<=" : "<") + bound + ";" +
|
||||
loopVar + "++) {\n" +
|
||||
reassign +
|
||||
code + "}\n";
|
||||
}
|
||||
}
|
||||
@@ -334,7 +341,7 @@ abstract class SourceOperation extends Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
String name = myCall.getMethodExpression().getReferenceName();
|
||||
if (name != null) {
|
||||
String unpluralized = StringUtil.unpluralize(name);
|
||||
|
||||
@@ -302,7 +302,6 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
}
|
||||
TerminalOperation terminal = getTerminal(operations);
|
||||
if (terminal == null) return;
|
||||
allOperations(operations).forEach(or -> or.myOperation.suggestNames(or.myInVar, or.myOutVar));
|
||||
PsiStatement statement = PsiTreeUtil.getParentOfType(terminalCall, PsiStatement.class);
|
||||
LOG.assertTrue(statement != null);
|
||||
CommentTracker ct = new CommentTracker();
|
||||
@@ -353,6 +352,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
}
|
||||
|
||||
private static void registerVariables(List<OperationRecord> operations, StreamToLoopReplacementContext context) {
|
||||
allOperations(operations).forEach(or -> or.myOperation.preprocessVariables(context, or.myInVar, or.myOutVar));
|
||||
allOperations(operations).map(or -> or.myOperation).forEach(op -> op.registerReusedElements(context::registerReusedElement));
|
||||
allOperations(operations).map(or -> or.myInVar).distinct().forEach(var -> var.register(context));
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ import java.util.List;
|
||||
/**
|
||||
* This class represents a variable which holds stream element. Its lifecycle is the following:
|
||||
* 1. Construction: fast, in case you don't need to perform a fix actually
|
||||
* 2. Gather name candidates (addBestNameCandidate/addOtherNameCandidate can be called).
|
||||
* 2. Preprocessing (addBestNameCandidate/addOtherNameCandidate/markFinal can be called).
|
||||
* 3. Register variable in {@code StreamToLoopReplacementContext}: actual variable name is assigned here
|
||||
* 4. Usage in code generation: getName()/getType() could be called.
|
||||
* 4. Usage in code generation: getName()/getType()/isFinal() could be called.
|
||||
*
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
@@ -55,6 +55,7 @@ class StreamVariable {
|
||||
|
||||
String myName;
|
||||
@NotNull String myType;
|
||||
boolean myFinal;
|
||||
|
||||
private Collection<String> myBestCandidates = new LinkedHashSet<>();
|
||||
private Collection<String> myOtherCandidates = new LinkedHashSet<>();
|
||||
@@ -68,6 +69,13 @@ class StreamVariable {
|
||||
myName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call if the resulting variable must be declared final (e.g. used in lambdas)
|
||||
*/
|
||||
public void markFinal() {
|
||||
myFinal = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register best name candidate for this variable (like lambda argument which was explicitly present in the original code).
|
||||
*
|
||||
@@ -120,6 +128,10 @@ class StreamVariable {
|
||||
return getType() + " " + getName() + "=" + initializer + ";\n";
|
||||
}
|
||||
|
||||
public boolean isFinal() {
|
||||
return myFinal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (myName == null) {
|
||||
|
||||
@@ -420,8 +420,8 @@ abstract class TerminalOperation extends Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
myAccumulator.suggestVariableName(inVar, 1);
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
myAccumulator.preprocessVariable(context, inVar, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -545,8 +545,8 @@ abstract class TerminalOperation extends Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
myFn.suggestVariableName(inVar, 0);
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
myFn.preprocessVariable(context, inVar, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -569,7 +569,7 @@ abstract class TerminalOperation extends Operation {
|
||||
interface CollectorOperation {
|
||||
// Non-trivial finishers are not supported
|
||||
default void transform(StreamToLoopReplacementContext context, String item) {}
|
||||
default void suggestNames(StreamVariable inVar, StreamVariable outVar) {}
|
||||
default void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {}
|
||||
default void registerReusedElements(Consumer<PsiElement> consumer) {}
|
||||
String getSupplier();
|
||||
String getAccumulatorUpdater(StreamVariable inVar, String acc);
|
||||
@@ -823,9 +823,9 @@ abstract class TerminalOperation extends Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
myKeyExtractor.suggestVariableName(inVar, 0);
|
||||
myValueExtractor.suggestVariableName(inVar, 0);
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
myKeyExtractor.preprocessVariable(context, inVar, 0);
|
||||
myValueExtractor.preprocessVariable(context, inVar, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -890,9 +890,9 @@ abstract class TerminalOperation extends Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
myKeyExtractor.suggestVariableName(inVar, 0);
|
||||
myCollector.suggestNames(inVar, outVar);
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
myKeyExtractor.preprocessVariable(context, inVar, 0);
|
||||
myCollector.preprocessVariables(context, inVar, outVar);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -931,9 +931,9 @@ abstract class TerminalOperation extends Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
myPredicate.suggestVariableName(inVar, 0);
|
||||
myCollector.suggestNames(inVar, outVar);
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
myPredicate.preprocessVariable(context, inVar, 0);
|
||||
myCollector.preprocessVariables(context, inVar, outVar);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -971,8 +971,8 @@ abstract class TerminalOperation extends Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
myMapper.suggestVariableName(inVar, 0);
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
myMapper.preprocessVariable(context, inVar, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1007,7 +1007,7 @@ abstract class TerminalOperation extends Operation {
|
||||
private void createVariable(StreamToLoopReplacementContext context, String item) {
|
||||
myMapper.transform(context, item);
|
||||
myVariable = new StreamVariable(myMapper.getResultType());
|
||||
myDownstream.suggestNames(myVariable, StreamVariable.STUB);
|
||||
myDownstream.preprocessVariables(context, myVariable, StreamVariable.STUB);
|
||||
myMapper.suggestFinalOutputNames(context, null, null).forEach(myVariable::addOtherNameCandidate);
|
||||
myVariable.register(context);
|
||||
}
|
||||
@@ -1067,8 +1067,8 @@ abstract class TerminalOperation extends Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
myFn.suggestVariableName(inVar, 0);
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
myFn.preprocessVariable(context, inVar, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1101,8 +1101,8 @@ abstract class TerminalOperation extends Operation {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
myOrigin.suggestNames(inVar, outVar);
|
||||
public void preprocessVariables(StreamToLoopReplacementContext context, StreamVariable inVar, StreamVariable outVar) {
|
||||
myOrigin.preprocessVariables(context, inVar, outVar);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// "Fix all 'Stream API call chain can be replaced with loop' problems in file" "true"
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.IntSummaryStatistics;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -71,11 +72,31 @@ public class Main {
|
||||
return stat;
|
||||
}
|
||||
|
||||
static void print(Supplier<String> messageSupplier) {
|
||||
System.out.println(messageSupplier.get());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(test());
|
||||
System.out.println(testUseName());
|
||||
System.out.println(testNested());
|
||||
System.out.println(testNestedRename());
|
||||
System.out.println(String.join("|", testNestedUseName()).length());
|
||||
|
||||
// convert to loop
|
||||
for (int j = 1; j < 2; j++) {
|
||||
int x1 = j;
|
||||
if (x1 > 0) {
|
||||
print(() -> "attempt #" + x1);
|
||||
}
|
||||
}
|
||||
|
||||
// convert to loop
|
||||
for (int x = 1; x < 2; x++) {
|
||||
if (x > 0) {
|
||||
int i = x + 1;
|
||||
print(() -> "attempt #" + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// "Fix all 'Stream API call chain can be replaced with loop' problems in file" "true"
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import java.util.IntSummaryStatistics;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -29,11 +30,28 @@ public class Main {
|
||||
return IntStream.range(0, 20).filter(x -> x > 2).flatMap(limit -> Stream.iterate(String.valueOf(limit), x -> x + limit).limit(limit).mapToInt(x -> x.length())).summaryStatistics();
|
||||
}
|
||||
|
||||
static void print(Supplier<String> messageSupplier) {
|
||||
System.out.println(messageSupplier.get());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(test());
|
||||
System.out.println(testUseName());
|
||||
System.out.println(testNested());
|
||||
System.out.println(testNestedRename());
|
||||
System.out.println(String.join("|", testNestedUseName()).length());
|
||||
|
||||
IntStream.range(1, 2) // convert to loop
|
||||
.filter(x -> x > 0)
|
||||
.forEach(i -> {
|
||||
print(() -> "attempt #" + i);
|
||||
});
|
||||
|
||||
IntStream.range(1, 2) // convert to loop
|
||||
.filter(x -> x > 0)
|
||||
.map(x -> x + 1)
|
||||
.forEach(i -> {
|
||||
print(() -> "attempt #" + i);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user