Properly implement flatMap with non-inlineable function (IDEA-200094)

Also more precise deletion of temporary variables
This commit is contained in:
Tagir Valeev
2018-10-08 16:00:09 +07:00
parent bdcfaf778e
commit e106580eeb
7 changed files with 67 additions and 17 deletions
@@ -175,10 +175,31 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
private void finishElement(PsiElement element) {
myCurrentFlow.finishElement(element);
if (element instanceof PsiStatement && !(element instanceof PsiReturnStatement)) {
addInstruction(new FinishElementInstruction(element));
List<DfaVariableValue> synthetics = getSynthetics(element);
FinishElementInstruction instruction = new FinishElementInstruction(element);
instruction.getVarsToFlush().addAll(synthetics);
addInstruction(instruction);
}
}
@NotNull
private List<DfaVariableValue> getSynthetics(PsiElement element) {
int startOffset = myCurrentFlow.getStartOffset(element).getInstructionOffset();
List<DfaVariableValue> synthetics = new ArrayList<>();
for (DfaValue value : myFactory.getValues()) {
if (value instanceof DfaVariableValue) {
DfaVariableValue var = (DfaVariableValue)value;
DfaVariableSource source = var.getSource();
if (source instanceof Synthetic) {
if (((Synthetic)source).myLocation >= startOffset) {
synthetics.add(var);
}
}
}
}
return synthetics;
}
@Override
public void visitErrorElement(PsiErrorElement element) {
throw new CannotAnalyzeException();
@@ -161,6 +161,4 @@ public interface DfaMemoryState {
boolean isEphemeral();
boolean isEmptyStack();
void cleanUpTempVariables();
}
@@ -606,12 +606,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
return myStack.isEmpty();
}
@Override
public void cleanUpTempVariables() {
List<DfaVariableValue> values = ContainerUtil.filter(myVariableStates.keySet(), ControlFlowAnalyzer::isTempVariable);
values.forEach(this::flushVariable);
}
@Override
public boolean castTopOfStack(@NotNull DfaPsiType type) {
DfaValue value = unwrap(peek());
@@ -415,7 +415,7 @@ public class StreamChainInliner implements CallInliner {
void before(CFGBuilder builder) {
if (myStreamSource == null) {
PsiExpression arg = myCall.getArgumentList().getExpressions()[0];
builder.pushExpression(arg).checkNotNull(arg, NullabilityProblemKind.passingNullableToNotNullParameter).pop();
builder.evaluateFunction(arg);
}
super.before(builder);
}
@@ -426,13 +426,16 @@ public class StreamChainInliner implements CallInliner {
builder.assignTo(myParameter).pop();
buildStreamCFG(builder, myChain, myStreamSource);
} else {
PsiExpression arg = myCall.getArgumentList().getExpressions()[0];
PsiType outType = StreamApiUtil.getStreamElementType(myCall.getType());
builder.pop()
.pushUnknown()
.ifConditionIs(true)
.doWhileUnknown()
.push(builder.getFactory().createTypeValue(outType, Nullability.UNKNOWN))
.chain(myNext::iteration)
builder.invokeFunction(1, arg, Nullability.NULLABLE)
.ifNotNull()
.pushUnknown()
.ifConditionIs(true)
.doWhileUnknown()
.push(builder.getFactory().createTypeValue(outType, Nullability.UNKNOWN))
.chain(myNext::iteration)
.end()
.end()
.end();
}
@@ -43,7 +43,6 @@ public class FinishElementInstruction extends Instruction {
state.flushVariable(value);
}
}
state.cleanUpTempVariables();
return nextInstruction(runner, state);
}
@@ -0,0 +1,34 @@
import java.util.*;
import java.util.stream.*;
// IDEA-200094
class Main {
void flatMapAlwaysNull(List<String> input) {
List<String> side = new ArrayList<>();
long count = input.stream().<String>flatMap(e -> {
if(!e.isEmpty()) side.add(e);
return null;
}).count();
if (side.isEmpty()) {} // not known
if (<warning descr="Condition 'count > 0' is always 'false'">count > 0</warning>) {}
}
public static void main(String[] args) {
new Main().myMethod();
}
private void myMethod() {
List<Integer> numberOne = Collections.singletonList(1);
List<Integer> collectionBeingModifiedSometimes = new ArrayList<>();
List<Integer> numberTwo = numberOne.stream().flatMap(entry -> {
if (Math.random() > 0.5) {
collectionBeingModifiedSometimes.add(999);
}
return Stream.of(entry * 2);
}).collect(Collectors.toList());
String text = collectionBeingModifiedSometimes.size() == 0 ? "empty" : "nonempty";
System.out.println("" + numberOne + " " + numberTwo + " " + text);
}
}
@@ -243,4 +243,5 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
doTest();
}
public void testObjectsEquals() { doTest(); }
public void testFlatMapSideEffect() { doTest(); }
}