Unroll short Stream.of(...) sequences (up to three elements)

Fixes IDEA-189143 Support unrolling of Stream.of(a, b, c)
This commit is contained in:
Tagir Valeev
2018-04-17 09:49:19 +07:00
parent 19cc2af06d
commit a83cedc58a
11 changed files with 180 additions and 123 deletions
@@ -25,6 +25,7 @@ import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ObjectUtils;
import one.util.streamex.IntStreamEx;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -41,13 +42,18 @@ import java.util.function.Consumer;
@SuppressWarnings("UnusedReturnValue")
public class CFGBuilder {
private final ControlFlowAnalyzer myAnalyzer;
private final Deque<JumpInstruction> myBranches = new ArrayDeque<>();
private final Deque<Runnable> myBranches = new ArrayDeque<>();
private final Map<PsiExpression, DfaVariableValue> myMethodRefQualifiers = new HashMap<>();
CFGBuilder(ControlFlowAnalyzer analyzer) {
myAnalyzer = analyzer;
}
private CFGBuilder add(Instruction instruction) {
myAnalyzer.addInstruction(instruction);
return this;
}
/**
* Generate instructions to push unknown DfaValue on stack.
* <p>
@@ -72,8 +78,7 @@ public class CFGBuilder {
* @return this builder
*/
public CFGBuilder pushNull() {
myAnalyzer.addInstruction(new PushInstruction(getFactory().getConstFactory().getNull(), null));
return this;
return add(new PushInstruction(getFactory().getConstFactory().getNull(), null));
}
/**
@@ -102,8 +107,7 @@ public class CFGBuilder {
* @return this builder
*/
public CFGBuilder pushForWrite(DfaVariableValue variable) {
myAnalyzer.addInstruction(new PushInstruction(variable, null, true));
return this;
return add(new PushInstruction(variable, null, true));
}
/**
@@ -117,8 +121,7 @@ public class CFGBuilder {
* @return this builder
*/
public CFGBuilder push(DfaValue value) {
myAnalyzer.addInstruction(new PushInstruction(value, null));
return this;
return add(new PushInstruction(value, null));
}
/**
@@ -131,8 +134,7 @@ public class CFGBuilder {
* @return this builder
*/
public CFGBuilder pop() {
myAnalyzer.addInstruction(new PopInstruction());
return this;
return add(new PopInstruction());
}
/**
@@ -145,8 +147,7 @@ public class CFGBuilder {
* @return this builder
*/
public CFGBuilder dup() {
myAnalyzer.addInstruction(new DupInstruction());
return this;
return add(new DupInstruction());
}
/**
@@ -161,8 +162,7 @@ public class CFGBuilder {
* @return this builder
*/
public CFGBuilder splice(int count, int... replacement) {
myAnalyzer.addInstruction(new SpliceInstruction(count, replacement));
return this;
return add(new SpliceInstruction(count, replacement));
}
/**
@@ -175,8 +175,7 @@ public class CFGBuilder {
* @return this builder
*/
public CFGBuilder swap() {
myAnalyzer.addInstruction(new SwapInstruction());
return this;
return add(new SwapInstruction());
}
/**
@@ -189,8 +188,7 @@ public class CFGBuilder {
* @return this builder
*/
public CFGBuilder objectOf() {
myAnalyzer.addInstruction(new ObjectOfInstruction());
return this;
return add(new ObjectOfInstruction());
}
/**
@@ -204,8 +202,7 @@ public class CFGBuilder {
* @return this builder
*/
public CFGBuilder isInstance(PsiMethodCallExpression anchor) {
myAnalyzer.addInstruction(new InstanceofInstruction(anchor));
return this;
return add(new InstanceofInstruction(anchor));
}
/**
@@ -239,8 +236,7 @@ public class CFGBuilder {
* @return this builder
*/
private CFGBuilder compare(IElementType relation) {
myAnalyzer.addInstruction(new BinopInstruction(relation, null, PsiType.BOOLEAN));
return this;
return add(new BinopInstruction(relation, null, PsiType.BOOLEAN));
}
/**
@@ -250,7 +246,7 @@ public class CFGBuilder {
* <p>
* Stack after: ...
* <p>
* The conditional block must end with {@link #endIf()} and may contain one {@link #elseBranch()} inside.
* The conditional block must end with {@link #end()} and may contain one {@link #elseBranch()} inside.
* Nested conditional blocks are acceptable.
*
* @param value a value condition must have to visit conditional block
@@ -258,9 +254,8 @@ public class CFGBuilder {
*/
public CFGBuilder ifConditionIs(boolean value) {
ConditionalGotoInstruction gotoInstruction = new ConditionalGotoInstruction(null, value, null);
myBranches.add(gotoInstruction);
myAnalyzer.addInstruction(gotoInstruction);
return this;
myBranches.add(() -> gotoInstruction.setOffset(myAnalyzer.getInstructionCount()));
return add(gotoInstruction);
}
/**
@@ -271,7 +266,7 @@ public class CFGBuilder {
* <p>
* Stack after: ...
* <p>
* The conditional block must end with {@link #endIf()} and may contain one {@link #elseBranch()} inside.
* The conditional block must end with {@link #end()} and may contain one {@link #elseBranch()} inside.
* Nested conditional blocks are acceptable.
*
* @param relation a relation to use to compare two stack values. Conditional block will be executed if "val1 relation val2" is true.
@@ -288,7 +283,7 @@ public class CFGBuilder {
* <p>
* Stack after: ...
* <p>
* The conditional block must end with {@link #endIf()} and may contain one {@link #elseBranch()} inside.
* The conditional block must end with {@link #end()} and may contain one {@link #elseBranch()} inside.
* Nested conditional blocks are acceptable.
*
* @return this builder
@@ -304,7 +299,7 @@ public class CFGBuilder {
* <p>
* Stack after: ...
* <p>
* The conditional block must end with {@link #endIf()} and may contain one {@link #elseBranch()} inside.
* The conditional block must end with {@link #end()} and may contain one {@link #elseBranch()} inside.
* Nested conditional blocks are acceptable.
*
* @return this builder
@@ -314,13 +309,12 @@ public class CFGBuilder {
}
/**
* Generate instructions to finish a conditional block started with {@link #ifCondition(IElementType)}, {@link #ifConditionIs(boolean)},
* {@link #ifNull()} or {@link #ifNotNull()}. Stack is unchanged.
* Generate instructions to finish a conditional block or a loop. Stack is unchanged.
*
* @return this builder
*/
public CFGBuilder endIf() {
myBranches.removeLast().setOffset(myAnalyzer.getInstructionCount());
public CFGBuilder end() {
myBranches.removeLast().run();
return this;
}
@@ -333,33 +327,21 @@ public class CFGBuilder {
*/
public CFGBuilder elseBranch() {
GotoInstruction gotoInstruction = new GotoInstruction(null);
myAnalyzer.addInstruction(gotoInstruction);
endIf();
myBranches.add(gotoInstruction);
add(gotoInstruction).end();
myBranches.add(() -> gotoInstruction.setOffset(myAnalyzer.getInstructionCount()));
return this;
}
/**
* Generate instructions to start a loop. Stack is unchanged. Loop must be terminated via {@link #endWhileUnknown()}.
* Generate instructions to start a loop. Stack is unchanged. Loop must be terminated via {@link #end()}.
* Nested loops are acceptable.
*
* @return this builder
*/
public CFGBuilder doWhile() {
public CFGBuilder doWhileUnknown() {
ConditionalGotoInstruction jump = new ConditionalGotoInstruction(null, false, null);
jump.setOffset(myAnalyzer.getInstructionCount());
myBranches.add(jump);
return this;
}
/**
* Generate instructions to end a loop started via {@link #doWhile()} by unknown condition. Stack is unchanged.
*
* @return this builder
*/
public CFGBuilder endWhileUnknown() {
pushUnknown();
myAnalyzer.addInstruction((ConditionalGotoInstruction)myBranches.removeLast());
myBranches.add(() -> pushUnknown().add(jump));
return this;
}
@@ -404,8 +386,7 @@ public class CFGBuilder {
* @return this builder
*/
public CFGBuilder flushFields() {
myAnalyzer.addInstruction(new FlushFieldsInstruction());
return this;
return add(new FlushFieldsInstruction());
}
/**
@@ -417,8 +398,7 @@ public class CFGBuilder {
* @return this builder
*/
public <T extends PsiElement> CFGBuilder checkNotNull(T expression, NullabilityProblemKind<T> kind) {
myAnalyzer.addInstruction(new CheckNotNullInstruction(kind.problem(expression)));
return this;
return add(new CheckNotNullInstruction(kind.problem(expression)));
}
/**
@@ -432,8 +412,7 @@ public class CFGBuilder {
* @return this builder
*/
public CFGBuilder assign() {
myAnalyzer.addInstruction(new AssignInstruction(null, null));
return this;
return add(new AssignInstruction(null, null));
}
/**
@@ -596,7 +575,7 @@ public class CFGBuilder {
if (qualifier == null) return false;
PsiType type = qualifier.getOperand().getType();
push(getFactory().createTypeValue(type, Nullness.NOT_NULL));
myAnalyzer.addInstruction(new InstanceofInstruction(methodRef, null, type));
add(new InstanceofInstruction(methodRef, null, type));
return true;
}
@@ -649,6 +628,45 @@ public class CFGBuilder {
return this;
}
public CFGBuilder loopOver(PsiExpression[] expressions, DfaVariableValue targetVariable) {
DfaValueFactory factory = getFactory();
if (expressions.length > ControlFlowAnalyzer.MAX_UNROLL_SIZE) {
DfaValue loopElement = null;
for (PsiExpression expression : expressions) {
pushExpression(expression);
DfaValue expressionValue = factory.createValue(expression);
if (expressionValue == null) {
expressionValue = factory.createTypeValue(expression.getType(), NullnessUtil.getExpressionNullness(expression));
}
loopElement = loopElement == null ? expressionValue : loopElement.union(expressionValue);
pop();
}
ConditionalGotoInstruction condGoto = new ConditionalGotoInstruction(null, false, null);
condGoto.setOffset(myAnalyzer.getInstructionCount());
myBranches.add(() -> pushUnknown().add(condGoto));
pushForWrite(targetVariable).push(loopElement).assign();
} else {
push(factory.getConstFactory().getSentinel());
for (PsiExpression expression : expressions) {
pushExpression(expression);
}
// Revert order
add(new SpliceInstruction(expressions.length, IntStreamEx.ofIndices(expressions).toArray()));
GotoInstruction gotoInstruction = new GotoInstruction(null);
gotoInstruction.setOffset(myAnalyzer.getInstructionCount());
dup().push(factory.getConstFactory().getSentinel()).compare(JavaTokenType.EQEQ);
ConditionalGotoInstruction condGoto = new ConditionalGotoInstruction(null, false, null);
add(condGoto);
assignTo(targetVariable);
myBranches.add(() -> {
add(gotoInstruction);
condGoto.setOffset(myAnalyzer.getInstructionCount());
pop();
});
}
return this;
}
/**
* Create a synthetic variable (not declared in the original code) to be used within this control flow.
*
@@ -51,7 +51,7 @@ import static com.intellij.psi.CommonClassNames.*;
public class ControlFlowAnalyzer extends JavaElementVisitor {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.ControlFlowAnalyzer");
public static final String ORG_JETBRAINS_ANNOTATIONS_CONTRACT = Contract.class.getName();
private static final int MAX_UNROLL_SIZE = 3;
static final int MAX_UNROLL_SIZE = 3;
private final PsiElement myCodeFragment;
private final boolean myIgnoreAssertions;
private final boolean myInlining;
@@ -892,10 +892,13 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
return true;
}
final boolean containsCalls = dfaLeft instanceof DfaVariableValue && ((DfaVariableValue)dfaLeft).containsCalls();
if (dfaLeft == dfaRight) {
return containsCalls || !isNegated;
return !isNegated || (dfaLeft instanceof DfaVariableValue && ((DfaVariableValue)dfaLeft).containsCalls());
}
DfaConstValue sentinel = getFactory().getConstFactory().getSentinel();
if (dfaLeft == sentinel || dfaRight == sentinel) {
return isNegated;
}
if (isNull(dfaLeft) && isNotNull(dfaRight) || isNull(dfaRight) && isNotNull(dfaLeft)) {
@@ -103,7 +103,7 @@ abstract class ComparatorModel {
@Override
void invoke(CFGBuilder builder) {
builder.dup().ifNotNull().chain(myDownstream::invoke).elseBranch().pop().endIf();
builder.dup().ifNotNull().chain(myDownstream::invoke).elseBranch().pop().end();
}
}
@@ -74,7 +74,7 @@ public class MapUpdateInliner implements CallInliner {
.push(builder.getFactory().createTypeValue(type, Nullness.NOT_NULL))
.swap()
.invokeFunction(2, function)
.endIf()
.end()
.flushFields();
return true;
}
@@ -92,7 +92,7 @@ public class MapUpdateInliner implements CallInliner {
.elseBranch()
.pop()
.push(builder.getFactory().createTypeValue(type, Nullness.NOT_NULL))
.endIf();
.end();
}
private static void inlineComputeIfPresent(@NotNull CFGBuilder builder,
@@ -110,7 +110,7 @@ public class MapUpdateInliner implements CallInliner {
.elseBranch()
.pop()
.pushNull()
.endIf();
.end();
}
private static void inlineCompute(@NotNull CFGBuilder builder,
@@ -80,7 +80,7 @@ public class OptionalChainInliner implements CallInliner {
.splice(2, 0, 1, 1) // stack: .. elseValue, optValue, optValue
.ifNotNull()
.swap() // stack: .. optValue, elseValue
.endIf()
.end()
.pop();
})
.register(OPTIONAL_OR_NULL, (builder, call) -> {
@@ -94,7 +94,7 @@ public class OptionalChainInliner implements CallInliner {
.ifNull()
.pop()
.invokeFunction(0, fn)
.endIf();
.end();
})
.register(OPTIONAL_IF_PRESENT, (builder, call) -> {
PsiExpression fn = call.getArgumentList().getExpressions()[0];
@@ -106,7 +106,7 @@ public class OptionalChainInliner implements CallInliner {
.elseBranch()
.pop()
.pushUnknown()
.endIf();
.end();
});
private static final CallMapper<BiConsumer<CFGBuilder, PsiExpression>> INTERMEDIATE_MAPPER =
@@ -122,19 +122,19 @@ public class OptionalChainInliner implements CallInliner {
.ifConditionIs(false)
.pop()
.pushNull()
.endIf()
.endIf())
.end()
.end())
.register(OPTIONAL_FLAT_MAP, (builder, function) -> builder
.dup()
.ifNotNull()
.chain(b -> invokeAndUnwrapOptional(b, 1, function))
.endIf())
.end())
.register(OPTIONAL_OR, (builder, function) -> builder
.dup()
.ifNull()
.pop()
.chain(b -> invokeAndUnwrapOptional(b, 0, function))
.endIf())
.end())
.register(GUAVA_TO_JAVA, (builder, stub) -> {/* no op */});
@Override
@@ -154,7 +154,7 @@ public class OptionalChainInliner implements CallInliner {
.push(factFactory.getFactValue(DfaFactType.OPTIONAL_PRESENCE, true))
.elseBranch()
.push(factFactory.getFactValue(DfaFactType.OPTIONAL_PRESENCE, false))
.endIf();
.end();
return true;
}
if (OPTIONAL_EMPTY.test(call)) {
@@ -200,7 +200,7 @@ public class OptionalChainInliner implements CallInliner {
.push(builder.getFactory().createTypeValue(optionalElementType, Nullness.NOT_NULL))
.elseBranch()
.pushNull()
.endIf()
.end()
.assignTo(builder.createTempVariable(optionalElementType));
return true;
}
@@ -251,7 +251,7 @@ public class OptionalChainInliner implements CallInliner {
.dup()
.ifNotNull()
.invokeFunction(1, function, resultNullness)
.endIf();
.end();
}
private static void inlineOf(CFGBuilder builder, PsiType optionalElementType, PsiMethodCallExpression qualifierCall) {
@@ -310,12 +310,12 @@ public class StreamChainInliner implements CallInliner {
@Override
void iteration(CFGBuilder builder) {
builder.invokeFunction(1, myFunction)
.ifConditionIs(!"allMatch".equals(myCall.getMethodExpression().getReferenceName()))
.pushForWrite(myResult)
.push(builder.getFactory().getBoolean("anyMatch".equals(myCall.getMethodExpression().getReferenceName())))
.assign()
.pop()
.endIf();
.ifConditionIs(!"allMatch".equals(myCall.getMethodExpression().getReferenceName()))
.pushForWrite(myResult)
.push(builder.getFactory().getBoolean("anyMatch".equals(myCall.getMethodExpression().getReferenceName())))
.assign()
.pop()
.end();
}
}
@@ -333,7 +333,7 @@ public class StreamChainInliner implements CallInliner {
.chain(myNext::iteration)
.elseBranch()
.pop()
.endIf();
.end();
}
}
@@ -415,13 +415,13 @@ public class StreamChainInliner implements CallInliner {
} else {
PsiType outType = StreamApiUtil.getStreamElementType(myCall.getType());
builder.pop()
.pushUnknown()
.ifConditionIs(true)
.doWhile()
.push(builder.getFactory().createTypeValue(outType, Nullness.UNKNOWN))
.chain(myNext::iteration)
.endWhileUnknown()
.endIf();
.pushUnknown()
.ifConditionIs(true)
.doWhileUnknown()
.push(builder.getFactory().createTypeValue(outType, Nullness.UNKNOWN))
.chain(myNext::iteration)
.end()
.end();
}
}
}
@@ -467,7 +467,7 @@ public class StreamChainInliner implements CallInliner {
.chain(myNext::iteration)
.elseBranch()
.pop()
.endIf();
.end();
}
}
@@ -595,7 +595,7 @@ public class StreamChainInliner implements CallInliner {
.ifConditionIs(true)
.push(builder.getFactory().getFactValue(DfaFactType.CAN_BE_NULL, false))
.invokeFunction(2, myMerger)
.endIf();
.end();
}
// Actual addition of Map element is unnecessary for current analysis
builder.pop();
@@ -620,10 +620,10 @@ public class StreamChainInliner implements CallInliner {
PsiExpression originalQualifier = firstStep.myCall.getMethodExpression().getQualifierExpression();
if (originalQualifier == null) return false;
builder.pushUnknown()
.ifConditionIs(true)
.chain(b -> buildStreamCFG(b, firstStep, originalQualifier))
.endIf()
.push(builder.getFactory().createTypeValue(call.getType(), Nullness.NOT_NULL));
.ifConditionIs(true)
.chain(b -> buildStreamCFG(b, firstStep, originalQualifier))
.end()
.push(builder.getFactory().createTypeValue(call.getType(), Nullness.NOT_NULL));
return true;
}
@@ -646,12 +646,11 @@ public class StreamChainInliner implements CallInliner {
builder
.evaluateFunction(fn)
.chain(firstStep::before)
.doWhile()
.doWhileUnknown()
.pushForWrite(builder.createTempVariable(inType))
.invokeFunction(0, fn)
.assign()
.chain(firstStep::iteration)
.endWhileUnknown();
.chain(firstStep::iteration).end();
return;
}
boolean empty = STREAM_EMPTY.test(sourceCall);
@@ -669,10 +668,9 @@ public class StreamChainInliner implements CallInliner {
}
} else {
builder
.pushExpression(originalQualifier)
.pop()
.chain(firstStep::before)
.chain(b -> makeMainLoop(b, firstStep, inType));
.loopOver(args, builder.createTempVariable(inType))
.chain(firstStep::iteration).end();
return;
}
}
@@ -693,14 +691,14 @@ public class StreamChainInliner implements CallInliner {
DfaValue qualifierValue = builder.getFactory().createValue(qualifierExpression);
if (qualifierValue != null) {
builder.pushExpression(qualifierExpression)
.chain(firstStep::before)
.checkNotNull(qualifierExpression, NullabilityProblemKind.passingNullableToNotNullParameter)
.pop()
.push(SpecialField.ARRAY_LENGTH.createValue(builder.getFactory(), qualifierValue))
.push(builder.getFactory().getInt(0))
.ifCondition(JavaTokenType.GT)
.chain(b -> makeMainLoop(b, firstStep, inType))
.endIf();
.chain(firstStep::before)
.checkNotNull(qualifierExpression, NullabilityProblemKind.passingNullableToNotNullParameter)
.pop()
.push(SpecialField.ARRAY_LENGTH.createValue(builder.getFactory(), qualifierValue))
.push(builder.getFactory().getInt(0))
.ifCondition(JavaTokenType.GT)
.chain(b -> makeMainLoop(b, firstStep, inType))
.end();
return;
}
}
@@ -709,14 +707,14 @@ public class StreamChainInliner implements CallInliner {
DfaValue qualifierValue = builder.getFactory().createValue(qualifierExpression);
if (qualifierValue != null) {
builder.pushExpression(qualifierExpression)
.chain(firstStep::before)
.checkNotNull(sourceCall, NullabilityProblemKind.callNPE)
.pop()
.push(SpecialField.COLLECTION_SIZE.createValue(builder.getFactory(), qualifierValue))
.push(builder.getFactory().getInt(0))
.ifCondition(JavaTokenType.GT)
.chain(b -> makeMainLoop(b, firstStep, inType))
.endIf();
.chain(firstStep::before)
.checkNotNull(sourceCall, NullabilityProblemKind.callNPE)
.pop()
.push(SpecialField.COLLECTION_SIZE.createValue(builder.getFactory(), qualifierValue))
.push(builder.getFactory().getInt(0))
.ifCondition(JavaTokenType.GT)
.chain(b -> makeMainLoop(b, firstStep, inType))
.end();
return;
}
}
@@ -728,16 +726,15 @@ public class StreamChainInliner implements CallInliner {
.pushUnknown()
.ifConditionIs(true)
.chain(b -> makeMainLoop(b, firstStep, inType))
.endIf();
.end();
}
private static void makeMainLoop(CFGBuilder builder, Step firstStep, PsiType inType) {
builder.doWhile()
.pushForWrite(builder.createTempVariable(inType))
.push(builder.getFactory().createTypeValue(inType, DfaPsiUtil.getTypeNullability(inType)))
.assign()
.chain(firstStep::iteration)
.endWhileUnknown();
builder.doWhileUnknown()
.pushForWrite(builder.createTempVariable(inType))
.push(builder.getFactory().createTypeValue(inType, DfaPsiUtil.getTypeNullability(inType)))
.assign()
.chain(firstStep::iteration).end();
}
static Step buildChain(PsiMethodCallExpression qualifierCall, Step terminalStep) {
@@ -21,6 +21,7 @@ import com.intellij.psi.*;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.ExpressionUtils;
import org.jetbrains.annotations.NonNls;
@@ -31,11 +32,13 @@ import java.util.Map;
public class DfaConstValue extends DfaValue {
private static final Throwable ourThrowable = new Throwable();
private static final Object SENTINEL = ObjectUtils.sentinel("SENTINEL");
public static class Factory {
private final DfaConstValue dfaNull;
private final DfaConstValue dfaFalse;
private final DfaConstValue dfaTrue;
private final DfaConstValue dfaFail;
private final DfaConstValue dfaSentinel;
private final DfaValueFactory myFactory;
private final Map<Object, DfaConstValue> myValues = ContainerUtil.newHashMap();
@@ -45,6 +48,7 @@ public class DfaConstValue extends DfaValue {
dfaFalse = new DfaConstValue(Boolean.FALSE, PsiType.BOOLEAN, factory, null);
dfaTrue = new DfaConstValue(Boolean.TRUE, PsiType.BOOLEAN, factory, null);
dfaFail = new DfaConstValue(ourThrowable, PsiType.VOID, factory, null);
dfaSentinel = new DfaConstValue(SENTINEL, PsiType.VOID, factory, null);
}
@Nullable
@@ -139,6 +143,14 @@ public class DfaConstValue extends DfaValue {
public DfaConstValue getNull() {
return dfaNull;
}
/**
* Sentinel value is special value used internally by dataflow. It cannot be stored to any variable, and equals to itself only
* @return sentinel value
*/
public DfaConstValue getSentinel() {
return dfaSentinel;
}
}
private final Object myValue;
@@ -42,6 +42,7 @@ public abstract class DfaValue {
*/
public DfaValue union(DfaValue other) {
if(this == other) return this;
if(this == DfaUnknownValue.getInstance() || other == DfaUnknownValue.getInstance()) return DfaUnknownValue.getInstance();
return myFactory.getFactFactory().createValue(DfaFactMap.fromDfaValue(this).union(DfaFactMap.fromDfaValue(other)));
}
@@ -125,4 +125,22 @@ public class StreamKnownSource {
System.out.println("Always");
}
}
void testLongVararg() {
IntStream.of(1, 2, 3, 4, 5).anyMatch(x -> <warning descr="Condition 'x > 6' is always 'false'">x > 6</warning>);
Stream.of("foo", "bar", "baz", "qux").filter(<warning descr="Method reference result is always 'true'">Objects::nonNull</warning>).forEach(System.out::println);
}
void testShortVararg(String a, String b) {
if(Stream.of(a, b).allMatch(Objects::isNull)) {
if(<warning descr="Condition 'a != null' is always 'false'">a != null</warning>) {
System.out.println("Impossible");
}
if(<warning descr="Condition 'b != null' is always 'false'">b != null</warning>) {
System.out.println("Impossible");
}
}
IntStream.of(1, 2).anyMatch(x -> <warning descr="Condition 'x > 6' is always 'false'">x > 6</warning>);
Stream.of("foo", "bar").filter(<warning descr="Method reference result is always 'true'">Objects::nonNull</warning>).forEach(System.out::println);
}
}
@@ -350,6 +350,14 @@ class OptionalGet {
// Non-empty stream: get() is fine
return Stream.of("foo", "bar", "baz").map(String::toUpperCase).max(Comparator.naturalOrder()).get();
}
void testStreamOfUnrolling(Optional<String> optionalOne, Optional<String> optionalTwo, Optional<String> optionalThree) {
if (Stream.of(optionalOne, optionalTwo).allMatch(Optional::isPresent)) {
System.out.println(optionalOne.get());
System.out.println(optionalTwo.get());
System.out.println(optionalThree.<warning descr="'Optional.get()' without 'isPresent()' check">get</warning>());
}
}
}
class CtorTest {