Skip to content

Commit 1ccfb9d

Browse files
committed
Add Queue.isEmpty/poll refinement
1 parent 68b7b63 commit 1ccfb9d

3 files changed

Lines changed: 91 additions & 2 deletions

File tree

checker/src/main/java/org/checkerframework/checker/nullness/NullnessNoInitAnnotatedTypeFactory.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ public class NullnessNoInitAnnotatedTypeFactory
113113
private final ExecutableElement mapGet =
114114
TreeUtils.getMethod("java.util.Map", "get", 1, processingEnv);
115115

116+
/** The Collection.isEmpty method. */
117+
private final ExecutableElement collectionIsEmpty =
118+
TreeUtils.getMethod("java.util.Collection", "isEmpty", 0, processingEnv);
119+
120+
/** The Queue.poll method. */
121+
private final ExecutableElement queuePoll =
122+
TreeUtils.getMethod("java.util.Queue", "poll", 0, processingEnv);
123+
116124
// List is in alphabetical order. If you update it, also update
117125
// ../../../../../../../../docs/manual/nullness-checker.tex
118126
// and make a pull request for variables NONNULL_ANNOTATIONS and BASE_COPYABLE_ANNOTATIONS in
@@ -1072,4 +1080,24 @@ private AnnotationMirror ensuresNonNullAnno(String expression) {
10721080
public boolean isMapGet(Node node) {
10731081
return NodeUtils.isMethodInvocation(node, mapGet, getProcessingEnv());
10741082
}
1083+
1084+
/**
1085+
* Returns true if {@code node} is an invocation of Collection.isEmpty.
1086+
*
1087+
* @param node a CFG node
1088+
* @return true if {@code node} is an invocation of Collection.isEmpty
1089+
*/
1090+
public boolean isCollectionIsEmpty(Node node) {
1091+
return NodeUtils.isMethodInvocation(node, collectionIsEmpty, getProcessingEnv());
1092+
}
1093+
1094+
/**
1095+
* Returns true if {@code node} is an invocation of Queue.poll.
1096+
*
1097+
* @param node a CFG node
1098+
* @return true if {@code node} is an invocation of Queue.poll
1099+
*/
1100+
public boolean isQueuePoll(Node node) {
1101+
return NodeUtils.isMethodInvocation(node, queuePoll, getProcessingEnv());
1102+
}
10751103
}

checker/src/main/java/org/checkerframework/checker/nullness/NullnessNoInitTransfer.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
import java.util.List;
4141
import java.util.Map;
42+
import java.util.Queue;
4243

4344
import javax.lang.model.element.AnnotationMirror;
4445
import javax.lang.model.element.ExecutableElement;
@@ -81,6 +82,14 @@ public class NullnessNoInitTransfer
8182
*/
8283
protected final AnnotatedDeclaredType MAP_TYPE;
8384

85+
/**
86+
* Java's Queue interface.
87+
*
88+
* <p>The qualifiers in this type don't matter -- it is not used as a fully-annotated
89+
* AnnotatedDeclaredType, but just passed to asSuper().
90+
*/
91+
protected final AnnotatedDeclaredType QUEUE_TYPE;
92+
8493
/** The type factory for the nullness analysis that was passed to the constructor. */
8594
protected final NullnessNoInitAnnotatedTypeFactory nullnessTypeFactory;
8695

@@ -130,6 +139,14 @@ public NullnessNoInitTransfer(NullnessNoInitAnalysis analysis) {
130139
nullnessTypeFactory,
131140
false);
132141

142+
QUEUE_TYPE =
143+
(AnnotatedDeclaredType)
144+
AnnotatedTypeMirror.createType(
145+
TypesUtils.typeFromClass(
146+
Queue.class, analysis.getTypes(), elements),
147+
nullnessTypeFactory,
148+
false);
149+
133150
nonNullAssumptionAfterInvocation =
134151
!analysis.getTypeFactory()
135152
.getChecker()
@@ -464,6 +481,34 @@ public TransferResult<NullnessNoInitValue, NullnessNoInitStore> visitMethodInvoc
464481
}
465482
}
466483

484+
// Handle Collection.isEmpty(), mark receiver as non-empty in the false branch
485+
if (nullnessTypeFactory.isCollectionIsEmpty(n)) {
486+
JavaExpression receiverExpr = JavaExpression.fromNode(receiver);
487+
if (CFAbstractStore.canInsertJavaExpression(receiverExpr)) {
488+
NullnessNoInitStore thenStore = result.getThenStore();
489+
NullnessNoInitStore elseStore = result.getElseStore();
490+
elseStore.insertValue(receiverExpr, NONNULL);
491+
return new ConditionalTransferResult<>(
492+
result.getResultValue(), thenStore, elseStore);
493+
}
494+
}
495+
496+
// Refine result to @NonNull if n is an invocation of Queue.poll(), the receiver is known to
497+
// be non-empty
498+
// and Queue element type is @NonNull
499+
if (nullnessTypeFactory.isQueuePoll(n)) {
500+
NullnessNoInitStore store = result.getRegularStore();
501+
JavaExpression receiverExpr = JavaExpression.fromNode(receiver);
502+
NullnessNoInitValue receiverValue = store.getValue(receiverExpr);
503+
if (receiverValue != null && receiverValue.getAnnotations().contains(NONNULL)) {
504+
AnnotatedTypeMirror receiverType = nullnessTypeFactory.getReceiverType(n.getTree());
505+
if (!isElementTypeNullable(receiverType)) {
506+
makeNonNull(result, n);
507+
refineToNonNull(result);
508+
}
509+
}
510+
}
511+
467512
return result;
468513
}
469514

@@ -485,6 +530,24 @@ private boolean isValueTypeNullable(AnnotatedTypeMirror mapOrSubtype) {
485530
return valueType.hasAnnotation(NULLABLE);
486531
}
487532

533+
/**
534+
* Returns true if queueType's element type (the E type argument to Queue) is @Nullable.
535+
*
536+
* @param queueOrSubtype the Queue type, or a subtype
537+
* @return true if queueType's element type is @Nullable
538+
*/
539+
private boolean isElementTypeNullable(AnnotatedTypeMirror queueOrSubtype) {
540+
AnnotatedDeclaredType queueType =
541+
AnnotatedTypes.asSuper(nullnessTypeFactory, queueOrSubtype, QUEUE_TYPE);
542+
int numTypeArguments = queueType.getTypeArguments().size();
543+
if (numTypeArguments != 1) {
544+
throw new TypeSystemError(
545+
"Wrong number %d of type arguments: %s", numTypeArguments, queueType);
546+
}
547+
AnnotatedTypeMirror elementType = queueType.getTypeArguments().get(0);
548+
return elementType.hasAnnotation(NULLABLE);
549+
}
550+
488551
@Override
489552
public TransferResult<NullnessNoInitValue, NullnessNoInitStore> visitReturn(
490553
ReturnNode n, TransferInput<NullnessNoInitValue, NullnessNoInitStore> in) {

checker/tests/nullness/IsEmptyPoll.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Test case for Issue 399:
22
// https://github.com/typetools/checker-framework/issues/399
33

4-
// @skip-test until the issue is fixed
5-
64
import org.checkerframework.checker.nullness.qual.NonNull;
75
import org.checkerframework.checker.nullness.qual.Nullable;
86

0 commit comments

Comments
 (0)