Optional presence fact should also report non-null (IDEA-201584)

This commit is contained in:
Tagir Valeev
2018-11-01 11:39:51 +07:00
parent ac16fe595e
commit a03a5cb0ea
6 changed files with 37 additions and 16 deletions
@@ -149,9 +149,9 @@ public abstract class ContractValue {
}
};
static final IndependentValue OPTIONAL_PRESENT =
new IndependentValue(factory -> factory.getFactValue(DfaFactType.OPTIONAL_PRESENCE, true), "present");
new IndependentValue(factory -> DfaOptionalSupport.getOptionalValue(factory, true), "present");
static final IndependentValue OPTIONAL_ABSENT =
new IndependentValue(factory -> factory.getFactValue(DfaFactType.OPTIONAL_PRESENCE, false), "empty");
new IndependentValue(factory -> DfaOptionalSupport.getOptionalValue(factory, false), "empty");
static final IndependentValue ZERO = new IndependentValue(factory -> factory.getInt(0), "0");
private final Function<? super DfaValueFactory, ? extends DfaValue> mySupplier;
@@ -199,10 +199,10 @@ class CustomMethodHandlers {
private static DfaValue ofNullable(DfaValue argument, DfaMemoryState state, DfaValueFactory factory) {
if (state.isNull(argument)) {
return factory.getFactValue(DfaFactType.OPTIONAL_PRESENCE, false);
return DfaOptionalSupport.getOptionalValue(factory, false);
}
if (state.isNotNull(argument)) {
return factory.getFactValue(DfaFactType.OPTIONAL_PRESENCE, true);
return DfaOptionalSupport.getOptionalValue(factory, true);
}
return null;
}
@@ -17,6 +17,8 @@ package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
@@ -83,6 +85,18 @@ public class DfaOptionalSupport {
return "get".equals(name) || "getAsDouble".equals(name) || "getAsInt".equals(name) || "getAsLong".equals(name);
}
/**
* Creates a DfaValue which represents present or absent optional (non-null)
* @param factory a value factory to use
* @param present whether the value should be present
* @return a DfaValue representing an Optional
*/
@NotNull
public static DfaValue getOptionalValue(DfaValueFactory factory, boolean present) {
DfaFactMap facts = DfaFactMap.EMPTY.with(DfaFactType.OPTIONAL_PRESENCE, present).with(DfaFactType.NULLABILITY, DfaNullability.NOT_NULL);
return factory.getFactFactory().createValue(facts);
}
private static class ReplaceOptionalCallFix implements LocalQuickFix {
private final String myTargetMethodName;
private final boolean myClearArguments;
@@ -17,7 +17,7 @@ package com.intellij.codeInspection.dataFlow.inliner;
import com.intellij.codeInsight.Nullability;
import com.intellij.codeInspection.dataFlow.CFGBuilder;
import com.intellij.codeInspection.dataFlow.DfaFactType;
import com.intellij.codeInspection.dataFlow.DfaOptionalSupport;
import com.intellij.codeInspection.dataFlow.NullabilityProblemKind;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
@@ -175,14 +175,14 @@ public class OptionalChainInliner implements CallInliner {
DfaValueFactory factFactory = builder.getFactory();
if (pushIntermediateOperationValue(builder, call)) {
builder.ifNotNull()
.push(factFactory.getFactValue(DfaFactType.OPTIONAL_PRESENCE, true))
.push(DfaOptionalSupport.getOptionalValue(factFactory, true))
.elseBranch()
.push(factFactory.getFactValue(DfaFactType.OPTIONAL_PRESENCE, false))
.push(DfaOptionalSupport.getOptionalValue(factFactory, false))
.end();
return true;
}
if (OPTIONAL_EMPTY.test(call)) {
builder.push(factFactory.getFactValue(DfaFactType.OPTIONAL_PRESENCE, false));
builder.push(DfaOptionalSupport.getOptionalValue(factFactory, false));
return true;
}
return false;
@@ -224,7 +224,7 @@ public class OptionalChainInliner implements CallInliner {
return true;
}
}
DfaValue presentOptional = builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, true);
DfaValue presentOptional = DfaOptionalSupport.getOptionalValue(builder.getFactory(), true);
builder
.pushExpression(expression)
.checkNotNull(dereferenceContext, problem)
@@ -294,16 +294,16 @@ public class OptionalChainInliner implements CallInliner {
.boxUnbox(argument, optionalElementType);
if ("of".equals(qualifierCall.getMethodExpression().getReferenceName())) {
builder.checkNotNull(argument, NullabilityProblemKind.passingNullableToNotNullParameter)
.push(builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, true), qualifierCall)
.push(DfaOptionalSupport.getOptionalValue(builder.getFactory(), true), qualifierCall)
.pop();
}
else {
builder
.dup()
.ifNull()
.push(builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, false), qualifierCall)
.push(DfaOptionalSupport.getOptionalValue(builder.getFactory(), false), qualifierCall)
.elseBranch()
.push(builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, true), qualifierCall)
.push(DfaOptionalSupport.getOptionalValue(builder.getFactory(), true), qualifierCall)
.end()
.pop();
}
@@ -262,12 +262,12 @@ public class StreamChainInliner implements CallInliner {
@Override
protected void pushInitialValue(CFGBuilder builder) {
builder.push(builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, false));
builder.push(DfaOptionalSupport.getOptionalValue(builder.getFactory(), false));
}
@Override
void iteration(CFGBuilder builder) {
DfaValue presentOptional = builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, true);
DfaValue presentOptional = DfaOptionalSupport.getOptionalValue(builder.getFactory(), true);
if (myFunction != null) {
builder.push(myResult)
.push(presentOptional)
@@ -291,7 +291,7 @@ public class StreamChainInliner implements CallInliner {
@Override
protected void pushInitialValue(CFGBuilder builder) {
builder.push(builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, false));
builder.push(DfaOptionalSupport.getOptionalValue(builder.getFactory(), false));
}
@Override
@@ -303,7 +303,7 @@ public class StreamChainInliner implements CallInliner {
@Override
void iteration(CFGBuilder builder) {
myComparatorModel.invoke(builder);
builder.assignAndPop(myResult, builder.getFactory().getFactValue(DfaFactType.OPTIONAL_PRESENCE, true));
builder.assignAndPop(myResult, DfaOptionalSupport.getOptionalValue(builder.getFactory(), true));
}
@Override
@@ -250,4 +250,11 @@ public class StreamInlining {
public static void testBoxingExplicit2() {
double s = Stream.of(1).mapToDouble(x -> x * x).sum();
}
void testOptionalNullity(List<Integer> groups) {
Optional<Integer> optional = groups.stream().findFirst();
if (<warning descr="Condition 'optional != null' is always 'true'">optional != null</warning> && optional.isPresent()) {
System.out.println("found");
}
}
}