mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-219109 Stream.of(...).anyMatch(Objects::isNull) does not affect nullability analysis
GitOrigin-RevId: ce9769eb34c87cf6bc07b0c526f799b9b4f9a324
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ae33c73266
commit
94b63d6ff0
+3
-1
@@ -954,7 +954,9 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
if (caseValue != null && expressionValue != null) {
|
||||
addInstruction(new PushInstruction(expressionValue, null));
|
||||
caseValue.accept(this);
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, PsiType.BOOLEAN));
|
||||
addInstruction(new BinopInstruction(
|
||||
TypeUtils.isJavaLangString(expressionValue.getType()) ? BinopInstruction.STRING_EQUALITY_BY_CONTENT :
|
||||
JavaTokenType.EQEQ, null, PsiType.BOOLEAN));
|
||||
}
|
||||
else {
|
||||
pushUnknown();
|
||||
|
||||
@@ -176,4 +176,12 @@ public interface DfaMemoryState {
|
||||
boolean isEphemeral();
|
||||
|
||||
boolean isEmptyStack();
|
||||
|
||||
/**
|
||||
* Returns true if two given values should be compared by content, rather than by reference.
|
||||
* @param dfaLeft left value
|
||||
* @param dfaRight right value
|
||||
* @return true if two given values should be compared by content, rather than by reference.
|
||||
*/
|
||||
boolean shouldCompareByEquals(DfaValue dfaLeft, DfaValue dfaRight);
|
||||
}
|
||||
|
||||
+19
-11
@@ -231,12 +231,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (DfaUtil.isComparedByEquals(value.getType()) && !DfaUtil.isComparedByEquals(var.getType())) {
|
||||
// Like Object x = "foo" or Object x = 5;
|
||||
TypeConstraint typeConstraint = TypeConstraint.empty().withInstanceofValue(myFactory.createDfaType(value.getType()));
|
||||
DfaFactMap facts = filterFactsOnAssignment(var, getFactMap(value).with(DfaFactType.TYPE_CONSTRAINT, typeConstraint));
|
||||
setVariableState(var, createVariableState(var).withFacts(facts));
|
||||
}
|
||||
else {
|
||||
setVariableState(var, isNull(value) ? state.withFact(DfaFactType.NULLABILITY, DfaNullability.NULL) : state);
|
||||
DfaRelationValue dfaEqual = myFactory.getRelationFactory().createRelation(var, RelationType.EQ, value);
|
||||
@@ -377,6 +371,15 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
return thisToThat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCompareByEquals(DfaValue dfaLeft, DfaValue dfaRight) {
|
||||
if (dfaLeft == dfaRight && !(dfaLeft instanceof DfaBoxedValue) && !(dfaLeft instanceof DfaConstValue)) {
|
||||
return false;
|
||||
}
|
||||
return !isNull(dfaLeft) && !isNull(dfaRight) &&
|
||||
DfaUtil.isComparedByEquals(getPsiType(dfaLeft)) && DfaUtil.isComparedByEquals(getPsiType(dfaRight));
|
||||
}
|
||||
|
||||
private static boolean isSuperValue(DfaValue superValue, DfaValue subValue) {
|
||||
if (superValue == DfaUnknownValue.getInstance() || superValue == subValue) return true;
|
||||
if (superValue instanceof DfaFactMapValue && subValue instanceof DfaFactMapValue) {
|
||||
@@ -1215,10 +1218,13 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiType getPsiType(@NotNull DfaValue value) {
|
||||
if (value instanceof DfaFactMapValue) {
|
||||
TypeConstraint constraint = ((DfaFactMapValue)value).get(DfaFactType.TYPE_CONSTRAINT);
|
||||
return constraint == null ? null : constraint.getPsiType();
|
||||
private PsiType getPsiType(@NotNull DfaValue value) {
|
||||
TypeConstraint constraint = getValueFact(value, DfaFactType.TYPE_CONSTRAINT);
|
||||
if (constraint != null) {
|
||||
PsiType type = constraint.getPsiType();
|
||||
if (type != null) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return value.getType();
|
||||
}
|
||||
@@ -1323,7 +1329,9 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
|
||||
private static boolean isPrimitive(DfaValue value) {
|
||||
return value instanceof DfaVariableValue && value.getType() instanceof PsiPrimitiveType;
|
||||
return value instanceof DfaVariableValue &&
|
||||
value.getType() instanceof PsiPrimitiveType &&
|
||||
!PsiType.VOID.equals(value.getType()); // void is used for temporary variables sometimes
|
||||
}
|
||||
|
||||
private static boolean preserveConstantDistinction(final Object c1, final Object c2) {
|
||||
|
||||
+4
-12
@@ -686,7 +686,8 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
DfaValue dfaLeft = memState.pop();
|
||||
|
||||
final IElementType opSign = instruction.getOperationSign();
|
||||
RelationType relationType = RelationType.fromElementType(opSign);
|
||||
RelationType relationType =
|
||||
RelationType.fromElementType(opSign == BinopInstruction.STRING_EQUALITY_BY_CONTENT ? JavaTokenType.EQEQ : opSign);
|
||||
if (relationType != null) {
|
||||
DfaInstructionState[] states = handleRelationBinop(instruction, runner, memState, dfaRight, dfaLeft, relationType);
|
||||
if (states != null) {
|
||||
@@ -757,8 +758,8 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
RelationType relationType) {
|
||||
DfaValueFactory factory = runner.getFactory();
|
||||
if((relationType == RelationType.EQ || relationType == RelationType.NE) &&
|
||||
(dfaLeft != dfaRight || dfaLeft instanceof DfaBoxedValue || dfaLeft instanceof DfaConstValue) &&
|
||||
isComparedByEquals(instruction.getExpression()) && !memState.isNull(dfaLeft) && !memState.isNull(dfaRight)) {
|
||||
instruction.getOperationSign() != BinopInstruction.STRING_EQUALITY_BY_CONTENT &&
|
||||
memState.shouldCompareByEquals(dfaLeft, dfaRight)) {
|
||||
ArrayList<DfaInstructionState> states = new ArrayList<>(2);
|
||||
DfaMemoryState equality = memState.createCopy();
|
||||
if (equality.applyCondition(factory.createCondition(dfaLeft, RelationType.EQ, dfaRight))) {
|
||||
@@ -801,15 +802,6 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
return states.toArray(DfaInstructionState.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
private static boolean isComparedByEquals(PsiExpression expression) {
|
||||
if (expression instanceof PsiBinaryExpression) {
|
||||
PsiExpression left = ((PsiBinaryExpression)expression).getLOperand();
|
||||
PsiExpression right = ((PsiBinaryExpression)expression).getROperand();
|
||||
return right != null && (DfaUtil.isComparedByEquals(left.getType()) && DfaUtil.isComparedByEquals(right.getType()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static RelationType[] splitRelation(RelationType relationType) {
|
||||
switch (relationType) {
|
||||
|
||||
+6
-1
@@ -32,7 +32,7 @@ import static com.intellij.psi.JavaTokenType.*;
|
||||
|
||||
public class BinopInstruction extends BranchingInstruction implements ExpressionPushingInstruction {
|
||||
private static final TokenSet ourSignificantOperations =
|
||||
TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, MINUS, AND, OR, XOR, PERC, DIV, ASTERISK, GTGT, GTGTGT, LTLT);
|
||||
TokenSet.create(EQ, EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, MINUS, AND, OR, XOR, PERC, DIV, ASTERISK, GTGT, GTGTGT, LTLT);
|
||||
|
||||
/**
|
||||
* A placeholder operation to model string concatenation inside loop:
|
||||
@@ -41,6 +41,11 @@ public class BinopInstruction extends BranchingInstruction implements Expression
|
||||
* so we use special operation for this case.
|
||||
*/
|
||||
public static final IElementType STRING_CONCAT_IN_LOOP = ASTERISK;
|
||||
/**
|
||||
* A special operation to express string comparison by content (like equals() method does).
|
||||
* Used to desugar switch statements
|
||||
*/
|
||||
public static final IElementType STRING_EQUALITY_BY_CONTENT = EQ;
|
||||
|
||||
private final IElementType myOperationSign;
|
||||
private final @Nullable PsiType myResultType;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import java.math.BigInteger;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class Test {
|
||||
@Nullable
|
||||
BigInteger calculate(@Nullable Boolean first, @Nullable BigInteger second, @Nullable BigInteger third) {
|
||||
if (Stream.of(first, second, third).anyMatch(Objects::isNull)) {
|
||||
return null;
|
||||
}
|
||||
return (first ? second : BigInteger.ZERO).add(third);
|
||||
}
|
||||
|
||||
void simpler(@Nullable Boolean b) {
|
||||
Object x = b;
|
||||
if (x != null) {
|
||||
System.out.println(b.hashCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -176,7 +176,7 @@ public class Super {
|
||||
|
||||
private void doTestSecond() {
|
||||
configure()
|
||||
assert myItems?.length == 0
|
||||
assert myItems == null || myItems.length == 0
|
||||
myFixture.completeBasic()
|
||||
checkResult()
|
||||
}
|
||||
|
||||
@@ -179,6 +179,7 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
|
||||
setupTypeUseAnnotations("foo", myFixture);
|
||||
doTest();
|
||||
}
|
||||
public void testStreamAnyMatchIsNull() { doTest(); }
|
||||
|
||||
public void testMapGetWithNotNullKeys() { doTestWithCustomAnnotations(); }
|
||||
public void testInferNestedForeachNullability() { doTestWithCustomAnnotations(); }
|
||||
|
||||
Reference in New Issue
Block a user