3939
4040import java .util .List ;
4141import java .util .Map ;
42+ import java .util .Queue ;
4243
4344import javax .lang .model .element .AnnotationMirror ;
4445import 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 ) {
0 commit comments