• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 Google Inc. All Rights Reserved.
3  *
4  * Modifications copyright (C) 2017 Uber Technologies, Inc.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7  * in compliance with the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software distributed under the License
12  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13  * or implied. See the License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.uber.nullaway.dataflow;
17 
18 import static com.google.common.base.Preconditions.checkNotNull;
19 import static com.uber.nullaway.Nullness.BOTTOM;
20 import static com.uber.nullaway.Nullness.NONNULL;
21 import static com.uber.nullaway.Nullness.NULLABLE;
22 import static javax.lang.model.element.ElementKind.EXCEPTION_PARAMETER;
23 import static org.checkerframework.nullaway.javacutil.TreeUtils.elementFromDeclaration;
24 
25 import com.google.common.base.Preconditions;
26 import com.google.errorprone.VisitorState;
27 import com.google.errorprone.suppliers.Supplier;
28 import com.google.errorprone.suppliers.Suppliers;
29 import com.google.errorprone.util.ASTHelpers;
30 import com.sun.tools.javac.code.Symbol;
31 import com.sun.tools.javac.code.Type;
32 import com.sun.tools.javac.code.TypeTag;
33 import com.uber.nullaway.Config;
34 import com.uber.nullaway.NullabilityUtil;
35 import com.uber.nullaway.Nullness;
36 import com.uber.nullaway.handlers.Handler;
37 import com.uber.nullaway.handlers.Handler.NullnessHint;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.function.Predicate;
42 import javax.annotation.CheckReturnValue;
43 import javax.annotation.Nullable;
44 import javax.lang.model.element.ElementKind;
45 import javax.lang.model.element.VariableElement;
46 import org.checkerframework.nullaway.dataflow.analysis.ConditionalTransferResult;
47 import org.checkerframework.nullaway.dataflow.analysis.ForwardTransferFunction;
48 import org.checkerframework.nullaway.dataflow.analysis.RegularTransferResult;
49 import org.checkerframework.nullaway.dataflow.analysis.TransferInput;
50 import org.checkerframework.nullaway.dataflow.analysis.TransferResult;
51 import org.checkerframework.nullaway.dataflow.cfg.UnderlyingAST;
52 import org.checkerframework.nullaway.dataflow.cfg.node.ArrayAccessNode;
53 import org.checkerframework.nullaway.dataflow.cfg.node.ArrayCreationNode;
54 import org.checkerframework.nullaway.dataflow.cfg.node.ArrayTypeNode;
55 import org.checkerframework.nullaway.dataflow.cfg.node.AssertionErrorNode;
56 import org.checkerframework.nullaway.dataflow.cfg.node.AssignmentNode;
57 import org.checkerframework.nullaway.dataflow.cfg.node.BitwiseAndNode;
58 import org.checkerframework.nullaway.dataflow.cfg.node.BitwiseComplementNode;
59 import org.checkerframework.nullaway.dataflow.cfg.node.BitwiseOrNode;
60 import org.checkerframework.nullaway.dataflow.cfg.node.BitwiseXorNode;
61 import org.checkerframework.nullaway.dataflow.cfg.node.BooleanLiteralNode;
62 import org.checkerframework.nullaway.dataflow.cfg.node.CaseNode;
63 import org.checkerframework.nullaway.dataflow.cfg.node.CharacterLiteralNode;
64 import org.checkerframework.nullaway.dataflow.cfg.node.ClassDeclarationNode;
65 import org.checkerframework.nullaway.dataflow.cfg.node.ClassNameNode;
66 import org.checkerframework.nullaway.dataflow.cfg.node.ConditionalAndNode;
67 import org.checkerframework.nullaway.dataflow.cfg.node.ConditionalNotNode;
68 import org.checkerframework.nullaway.dataflow.cfg.node.ConditionalOrNode;
69 import org.checkerframework.nullaway.dataflow.cfg.node.DoubleLiteralNode;
70 import org.checkerframework.nullaway.dataflow.cfg.node.EqualToNode;
71 import org.checkerframework.nullaway.dataflow.cfg.node.ExplicitThisNode;
72 import org.checkerframework.nullaway.dataflow.cfg.node.FieldAccessNode;
73 import org.checkerframework.nullaway.dataflow.cfg.node.FloatLiteralNode;
74 import org.checkerframework.nullaway.dataflow.cfg.node.FloatingDivisionNode;
75 import org.checkerframework.nullaway.dataflow.cfg.node.FloatingRemainderNode;
76 import org.checkerframework.nullaway.dataflow.cfg.node.FunctionalInterfaceNode;
77 import org.checkerframework.nullaway.dataflow.cfg.node.GreaterThanNode;
78 import org.checkerframework.nullaway.dataflow.cfg.node.GreaterThanOrEqualNode;
79 import org.checkerframework.nullaway.dataflow.cfg.node.ImplicitThisNode;
80 import org.checkerframework.nullaway.dataflow.cfg.node.InstanceOfNode;
81 import org.checkerframework.nullaway.dataflow.cfg.node.IntegerDivisionNode;
82 import org.checkerframework.nullaway.dataflow.cfg.node.IntegerLiteralNode;
83 import org.checkerframework.nullaway.dataflow.cfg.node.IntegerRemainderNode;
84 import org.checkerframework.nullaway.dataflow.cfg.node.LambdaResultExpressionNode;
85 import org.checkerframework.nullaway.dataflow.cfg.node.LeftShiftNode;
86 import org.checkerframework.nullaway.dataflow.cfg.node.LessThanNode;
87 import org.checkerframework.nullaway.dataflow.cfg.node.LessThanOrEqualNode;
88 import org.checkerframework.nullaway.dataflow.cfg.node.LocalVariableNode;
89 import org.checkerframework.nullaway.dataflow.cfg.node.LongLiteralNode;
90 import org.checkerframework.nullaway.dataflow.cfg.node.MarkerNode;
91 import org.checkerframework.nullaway.dataflow.cfg.node.MethodAccessNode;
92 import org.checkerframework.nullaway.dataflow.cfg.node.MethodInvocationNode;
93 import org.checkerframework.nullaway.dataflow.cfg.node.NarrowingConversionNode;
94 import org.checkerframework.nullaway.dataflow.cfg.node.Node;
95 import org.checkerframework.nullaway.dataflow.cfg.node.NotEqualNode;
96 import org.checkerframework.nullaway.dataflow.cfg.node.NullChkNode;
97 import org.checkerframework.nullaway.dataflow.cfg.node.NullLiteralNode;
98 import org.checkerframework.nullaway.dataflow.cfg.node.NumericalAdditionNode;
99 import org.checkerframework.nullaway.dataflow.cfg.node.NumericalMinusNode;
100 import org.checkerframework.nullaway.dataflow.cfg.node.NumericalMultiplicationNode;
101 import org.checkerframework.nullaway.dataflow.cfg.node.NumericalPlusNode;
102 import org.checkerframework.nullaway.dataflow.cfg.node.NumericalSubtractionNode;
103 import org.checkerframework.nullaway.dataflow.cfg.node.ObjectCreationNode;
104 import org.checkerframework.nullaway.dataflow.cfg.node.PackageNameNode;
105 import org.checkerframework.nullaway.dataflow.cfg.node.ParameterizedTypeNode;
106 import org.checkerframework.nullaway.dataflow.cfg.node.PrimitiveTypeNode;
107 import org.checkerframework.nullaway.dataflow.cfg.node.ReturnNode;
108 import org.checkerframework.nullaway.dataflow.cfg.node.ShortLiteralNode;
109 import org.checkerframework.nullaway.dataflow.cfg.node.SignedRightShiftNode;
110 import org.checkerframework.nullaway.dataflow.cfg.node.StringConcatenateAssignmentNode;
111 import org.checkerframework.nullaway.dataflow.cfg.node.StringConcatenateNode;
112 import org.checkerframework.nullaway.dataflow.cfg.node.StringConversionNode;
113 import org.checkerframework.nullaway.dataflow.cfg.node.StringLiteralNode;
114 import org.checkerframework.nullaway.dataflow.cfg.node.SuperNode;
115 import org.checkerframework.nullaway.dataflow.cfg.node.SwitchExpressionNode;
116 import org.checkerframework.nullaway.dataflow.cfg.node.SynchronizedNode;
117 import org.checkerframework.nullaway.dataflow.cfg.node.TernaryExpressionNode;
118 import org.checkerframework.nullaway.dataflow.cfg.node.ThisNode;
119 import org.checkerframework.nullaway.dataflow.cfg.node.ThrowNode;
120 import org.checkerframework.nullaway.dataflow.cfg.node.TypeCastNode;
121 import org.checkerframework.nullaway.dataflow.cfg.node.UnsignedRightShiftNode;
122 import org.checkerframework.nullaway.dataflow.cfg.node.VariableDeclarationNode;
123 import org.checkerframework.nullaway.dataflow.cfg.node.WideningConversionNode;
124 
125 /**
126  * transfer functions for our access path nullness dataflow analysis
127  *
128  * <p>Based on code originally from Error Prone (see {@link
129  * com.google.errorprone.dataflow.nullnesspropagation.AbstractNullnessPropagationTransfer} and
130  * {@link com.google.errorprone.dataflow.nullnesspropagation.NullnessPropagationTransfer})
131  */
132 public class AccessPathNullnessPropagation
133     implements ForwardTransferFunction<Nullness, NullnessStore> {
134 
135   private static final boolean NO_STORE_CHANGE = false;
136 
137   private static final Supplier<Type> SET_TYPE_SUPPLIER = Suppliers.typeFromString("java.util.Set");
138 
139   private static final Supplier<Type> ITERATOR_TYPE_SUPPLIER =
140       Suppliers.typeFromString("java.util.Iterator");
141 
142   private final Nullness defaultAssumption;
143 
144   private final Predicate<MethodInvocationNode> methodReturnsNonNull;
145 
146   private final VisitorState state;
147 
148   private final AccessPath.AccessPathContext apContext;
149 
150   private final Config config;
151 
152   private final Handler handler;
153 
154   private final NullnessStoreInitializer nullnessStoreInitializer;
155 
AccessPathNullnessPropagation( Nullness defaultAssumption, Predicate<MethodInvocationNode> methodReturnsNonNull, VisitorState state, AccessPath.AccessPathContext apContext, Config config, Handler handler, NullnessStoreInitializer nullnessStoreInitializer)156   public AccessPathNullnessPropagation(
157       Nullness defaultAssumption,
158       Predicate<MethodInvocationNode> methodReturnsNonNull,
159       VisitorState state,
160       AccessPath.AccessPathContext apContext,
161       Config config,
162       Handler handler,
163       NullnessStoreInitializer nullnessStoreInitializer) {
164     this.defaultAssumption = defaultAssumption;
165     this.methodReturnsNonNull = methodReturnsNonNull;
166     this.state = state;
167     this.apContext = apContext;
168     this.config = config;
169     this.handler = handler;
170     this.nullnessStoreInitializer = nullnessStoreInitializer;
171   }
172 
values(final TransferInput<Nullness, NullnessStore> input)173   private static SubNodeValues values(final TransferInput<Nullness, NullnessStore> input) {
174     return input::getValueOfSubNode;
175   }
176 
177   /**
178    * @param node CFG node
179    * @return if node is an {@link AssignmentNode} unwraps it to its LHS. otherwise returns node
180    */
unwrapAssignExpr(Node node)181   private static Node unwrapAssignExpr(Node node) {
182     if (node instanceof AssignmentNode) {
183       // in principle, we could separately handle the LHS and RHS and add new facts
184       // about both.  For now, just handle the LHS as that seems like the more common
185       // case (see https://github.com/uber/NullAway/issues/97)
186       return ((AssignmentNode) node).getTarget();
187     } else {
188       return node;
189     }
190   }
191 
192   @Override
initialStore( UnderlyingAST underlyingAST, List<LocalVariableNode> parameters)193   public NullnessStore initialStore(
194       UnderlyingAST underlyingAST, List<LocalVariableNode> parameters) {
195     return nullnessStoreInitializer.getInitialStore(
196         underlyingAST, parameters, handler, state.context, state.getTypes(), config);
197   }
198 
199   @Override
visitShortLiteral( ShortLiteralNode shortLiteralNode, TransferInput<Nullness, NullnessStore> input)200   public TransferResult<Nullness, NullnessStore> visitShortLiteral(
201       ShortLiteralNode shortLiteralNode, TransferInput<Nullness, NullnessStore> input) {
202     return noStoreChanges(NONNULL, input);
203   }
204 
205   @Override
visitIntegerLiteral( IntegerLiteralNode integerLiteralNode, TransferInput<Nullness, NullnessStore> input)206   public TransferResult<Nullness, NullnessStore> visitIntegerLiteral(
207       IntegerLiteralNode integerLiteralNode, TransferInput<Nullness, NullnessStore> input) {
208     return noStoreChanges(NONNULL, input);
209   }
210 
211   @Override
visitLongLiteral( LongLiteralNode longLiteralNode, TransferInput<Nullness, NullnessStore> input)212   public TransferResult<Nullness, NullnessStore> visitLongLiteral(
213       LongLiteralNode longLiteralNode, TransferInput<Nullness, NullnessStore> input) {
214     return noStoreChanges(NONNULL, input);
215   }
216 
217   @Override
visitFloatLiteral( FloatLiteralNode floatLiteralNode, TransferInput<Nullness, NullnessStore> input)218   public TransferResult<Nullness, NullnessStore> visitFloatLiteral(
219       FloatLiteralNode floatLiteralNode, TransferInput<Nullness, NullnessStore> input) {
220     return noStoreChanges(NONNULL, input);
221   }
222 
223   @Override
visitDoubleLiteral( DoubleLiteralNode doubleLiteralNode, TransferInput<Nullness, NullnessStore> input)224   public TransferResult<Nullness, NullnessStore> visitDoubleLiteral(
225       DoubleLiteralNode doubleLiteralNode, TransferInput<Nullness, NullnessStore> input) {
226     return noStoreChanges(NONNULL, input);
227   }
228 
229   @Override
visitBooleanLiteral( BooleanLiteralNode booleanLiteralNode, TransferInput<Nullness, NullnessStore> input)230   public TransferResult<Nullness, NullnessStore> visitBooleanLiteral(
231       BooleanLiteralNode booleanLiteralNode, TransferInput<Nullness, NullnessStore> input) {
232     return noStoreChanges(NONNULL, input);
233   }
234 
235   @Override
visitCharacterLiteral( CharacterLiteralNode characterLiteralNode, TransferInput<Nullness, NullnessStore> input)236   public TransferResult<Nullness, NullnessStore> visitCharacterLiteral(
237       CharacterLiteralNode characterLiteralNode, TransferInput<Nullness, NullnessStore> input) {
238     return noStoreChanges(NONNULL, input);
239   }
240 
241   @Override
visitStringLiteral( StringLiteralNode stringLiteralNode, TransferInput<Nullness, NullnessStore> input)242   public TransferResult<Nullness, NullnessStore> visitStringLiteral(
243       StringLiteralNode stringLiteralNode, TransferInput<Nullness, NullnessStore> input) {
244     return noStoreChanges(NONNULL, input);
245   }
246 
247   @Override
visitNullLiteral( NullLiteralNode nullLiteralNode, TransferInput<Nullness, NullnessStore> input)248   public TransferResult<Nullness, NullnessStore> visitNullLiteral(
249       NullLiteralNode nullLiteralNode, TransferInput<Nullness, NullnessStore> input) {
250     // let's be sane here and return null
251     return new RegularTransferResult<>(Nullness.NULL, input.getRegularStore());
252   }
253 
254   @Override
visitNumericalMinus( NumericalMinusNode numericalMinusNode, TransferInput<Nullness, NullnessStore> input)255   public TransferResult<Nullness, NullnessStore> visitNumericalMinus(
256       NumericalMinusNode numericalMinusNode, TransferInput<Nullness, NullnessStore> input) {
257     return noStoreChanges(NONNULL, input);
258   }
259 
260   @Override
visitNumericalPlus( NumericalPlusNode numericalPlusNode, TransferInput<Nullness, NullnessStore> input)261   public TransferResult<Nullness, NullnessStore> visitNumericalPlus(
262       NumericalPlusNode numericalPlusNode, TransferInput<Nullness, NullnessStore> input) {
263     return noStoreChanges(NONNULL, input);
264   }
265 
266   @Override
visitBitwiseComplement( BitwiseComplementNode bitwiseComplementNode, TransferInput<Nullness, NullnessStore> input)267   public TransferResult<Nullness, NullnessStore> visitBitwiseComplement(
268       BitwiseComplementNode bitwiseComplementNode, TransferInput<Nullness, NullnessStore> input) {
269     return noStoreChanges(NONNULL, input);
270   }
271 
272   @Override
visitNullChk( NullChkNode nullChkNode, TransferInput<Nullness, NullnessStore> input)273   public TransferResult<Nullness, NullnessStore> visitNullChk(
274       NullChkNode nullChkNode, TransferInput<Nullness, NullnessStore> input) {
275     SubNodeValues values = values(input);
276     Nullness nullness =
277         hasPrimitiveType(nullChkNode) ? NONNULL : values.valueOfSubNode(nullChkNode.getOperand());
278     return noStoreChanges(nullness, input);
279   }
280 
281   @Override
visitStringConcatenate( StringConcatenateNode stringConcatenateNode, TransferInput<Nullness, NullnessStore> input)282   public TransferResult<Nullness, NullnessStore> visitStringConcatenate(
283       StringConcatenateNode stringConcatenateNode, TransferInput<Nullness, NullnessStore> input) {
284     // concatenation always returns non-null
285     return noStoreChanges(NONNULL, input);
286   }
287 
288   @Override
visitNumericalAddition( NumericalAdditionNode numericalAdditionNode, TransferInput<Nullness, NullnessStore> input)289   public TransferResult<Nullness, NullnessStore> visitNumericalAddition(
290       NumericalAdditionNode numericalAdditionNode, TransferInput<Nullness, NullnessStore> input) {
291     return noStoreChanges(NONNULL, input);
292   }
293 
294   @Override
visitNumericalSubtraction( NumericalSubtractionNode numericalSubtractionNode, TransferInput<Nullness, NullnessStore> input)295   public TransferResult<Nullness, NullnessStore> visitNumericalSubtraction(
296       NumericalSubtractionNode numericalSubtractionNode,
297       TransferInput<Nullness, NullnessStore> input) {
298     return noStoreChanges(NONNULL, input);
299   }
300 
301   @Override
visitNumericalMultiplication( NumericalMultiplicationNode numericalMultiplicationNode, TransferInput<Nullness, NullnessStore> input)302   public TransferResult<Nullness, NullnessStore> visitNumericalMultiplication(
303       NumericalMultiplicationNode numericalMultiplicationNode,
304       TransferInput<Nullness, NullnessStore> input) {
305     return noStoreChanges(NONNULL, input);
306   }
307 
308   @Override
visitIntegerDivision( IntegerDivisionNode integerDivisionNode, TransferInput<Nullness, NullnessStore> input)309   public TransferResult<Nullness, NullnessStore> visitIntegerDivision(
310       IntegerDivisionNode integerDivisionNode, TransferInput<Nullness, NullnessStore> input) {
311     return noStoreChanges(NONNULL, input);
312   }
313 
314   @Override
visitFloatingDivision( FloatingDivisionNode floatingDivisionNode, TransferInput<Nullness, NullnessStore> input)315   public TransferResult<Nullness, NullnessStore> visitFloatingDivision(
316       FloatingDivisionNode floatingDivisionNode, TransferInput<Nullness, NullnessStore> input) {
317     return noStoreChanges(NONNULL, input);
318   }
319 
320   @Override
visitIntegerRemainder( IntegerRemainderNode integerRemainderNode, TransferInput<Nullness, NullnessStore> input)321   public TransferResult<Nullness, NullnessStore> visitIntegerRemainder(
322       IntegerRemainderNode integerRemainderNode, TransferInput<Nullness, NullnessStore> input) {
323     return noStoreChanges(NONNULL, input);
324   }
325 
326   @Override
visitFloatingRemainder( FloatingRemainderNode floatingRemainderNode, TransferInput<Nullness, NullnessStore> input)327   public TransferResult<Nullness, NullnessStore> visitFloatingRemainder(
328       FloatingRemainderNode floatingRemainderNode, TransferInput<Nullness, NullnessStore> input) {
329     return noStoreChanges(NONNULL, input);
330   }
331 
332   @Override
visitLeftShift( LeftShiftNode leftShiftNode, TransferInput<Nullness, NullnessStore> input)333   public TransferResult<Nullness, NullnessStore> visitLeftShift(
334       LeftShiftNode leftShiftNode, TransferInput<Nullness, NullnessStore> input) {
335     return noStoreChanges(NONNULL, input);
336   }
337 
338   @Override
visitSignedRightShift( SignedRightShiftNode signedRightShiftNode, TransferInput<Nullness, NullnessStore> input)339   public TransferResult<Nullness, NullnessStore> visitSignedRightShift(
340       SignedRightShiftNode signedRightShiftNode, TransferInput<Nullness, NullnessStore> input) {
341     return noStoreChanges(NONNULL, input);
342   }
343 
344   @Override
visitUnsignedRightShift( UnsignedRightShiftNode unsignedRightShiftNode, TransferInput<Nullness, NullnessStore> input)345   public TransferResult<Nullness, NullnessStore> visitUnsignedRightShift(
346       UnsignedRightShiftNode unsignedRightShiftNode, TransferInput<Nullness, NullnessStore> input) {
347     return noStoreChanges(NONNULL, input);
348   }
349 
350   @Override
visitBitwiseAnd( BitwiseAndNode bitwiseAndNode, TransferInput<Nullness, NullnessStore> input)351   public TransferResult<Nullness, NullnessStore> visitBitwiseAnd(
352       BitwiseAndNode bitwiseAndNode, TransferInput<Nullness, NullnessStore> input) {
353     return noStoreChanges(NONNULL, input);
354   }
355 
356   @Override
visitBitwiseOr( BitwiseOrNode bitwiseOrNode, TransferInput<Nullness, NullnessStore> input)357   public TransferResult<Nullness, NullnessStore> visitBitwiseOr(
358       BitwiseOrNode bitwiseOrNode, TransferInput<Nullness, NullnessStore> input) {
359     return noStoreChanges(NONNULL, input);
360   }
361 
362   @Override
visitBitwiseXor( BitwiseXorNode bitwiseXorNode, TransferInput<Nullness, NullnessStore> input)363   public TransferResult<Nullness, NullnessStore> visitBitwiseXor(
364       BitwiseXorNode bitwiseXorNode, TransferInput<Nullness, NullnessStore> input) {
365     return noStoreChanges(NONNULL, input);
366   }
367 
368   @Override
visitStringConcatenateAssignment( StringConcatenateAssignmentNode stringConcatenateAssignmentNode, TransferInput<Nullness, NullnessStore> input)369   public TransferResult<Nullness, NullnessStore> visitStringConcatenateAssignment(
370       StringConcatenateAssignmentNode stringConcatenateAssignmentNode,
371       TransferInput<Nullness, NullnessStore> input) {
372     return noStoreChanges(NULLABLE, input);
373   }
374 
375   @Override
visitLessThan( LessThanNode lessThanNode, TransferInput<Nullness, NullnessStore> input)376   public TransferResult<Nullness, NullnessStore> visitLessThan(
377       LessThanNode lessThanNode, TransferInput<Nullness, NullnessStore> input) {
378     return noStoreChanges(NONNULL, input);
379   }
380 
381   @Override
visitLessThanOrEqual( LessThanOrEqualNode lessThanOrEqualNode, TransferInput<Nullness, NullnessStore> input)382   public TransferResult<Nullness, NullnessStore> visitLessThanOrEqual(
383       LessThanOrEqualNode lessThanOrEqualNode, TransferInput<Nullness, NullnessStore> input) {
384     return noStoreChanges(NONNULL, input);
385   }
386 
387   @Override
visitGreaterThan( GreaterThanNode greaterThanNode, TransferInput<Nullness, NullnessStore> input)388   public TransferResult<Nullness, NullnessStore> visitGreaterThan(
389       GreaterThanNode greaterThanNode, TransferInput<Nullness, NullnessStore> input) {
390     return noStoreChanges(NONNULL, input);
391   }
392 
393   @Override
visitGreaterThanOrEqual( GreaterThanOrEqualNode greaterThanOrEqualNode, TransferInput<Nullness, NullnessStore> input)394   public TransferResult<Nullness, NullnessStore> visitGreaterThanOrEqual(
395       GreaterThanOrEqualNode greaterThanOrEqualNode, TransferInput<Nullness, NullnessStore> input) {
396     return noStoreChanges(NONNULL, input);
397   }
398 
399   @Override
visitEqualTo( EqualToNode equalToNode, TransferInput<Nullness, NullnessStore> input)400   public TransferResult<Nullness, NullnessStore> visitEqualTo(
401       EqualToNode equalToNode, TransferInput<Nullness, NullnessStore> input) {
402     ReadableUpdates thenUpdates = new ReadableUpdates();
403     ReadableUpdates elseUpdates = new ReadableUpdates();
404     handleEqualityComparison(
405         true,
406         equalToNode.getLeftOperand(),
407         equalToNode.getRightOperand(),
408         values(input),
409         thenUpdates,
410         elseUpdates);
411     ResultingStore thenStore = updateStore(input.getThenStore(), thenUpdates);
412     ResultingStore elseStore = updateStore(input.getElseStore(), elseUpdates);
413     return conditionalResult(
414         thenStore.store, elseStore.store, thenStore.storeChanged || elseStore.storeChanged);
415   }
416 
417   @Override
visitNotEqual( NotEqualNode notEqualNode, TransferInput<Nullness, NullnessStore> input)418   public TransferResult<Nullness, NullnessStore> visitNotEqual(
419       NotEqualNode notEqualNode, TransferInput<Nullness, NullnessStore> input) {
420     ReadableUpdates thenUpdates = new ReadableUpdates();
421     ReadableUpdates elseUpdates = new ReadableUpdates();
422     handleEqualityComparison(
423         false,
424         notEqualNode.getLeftOperand(),
425         notEqualNode.getRightOperand(),
426         values(input),
427         thenUpdates,
428         elseUpdates);
429     ResultingStore thenStore = updateStore(input.getThenStore(), thenUpdates);
430     ResultingStore elseStore = updateStore(input.getElseStore(), elseUpdates);
431     return conditionalResult(
432         thenStore.store, elseStore.store, thenStore.storeChanged || elseStore.storeChanged);
433   }
434 
handleEqualityComparison( boolean equalTo, Node leftNode, Node rightNode, SubNodeValues inputs, Updates thenUpdates, Updates elseUpdates)435   private void handleEqualityComparison(
436       boolean equalTo,
437       Node leftNode,
438       Node rightNode,
439       SubNodeValues inputs,
440       Updates thenUpdates,
441       Updates elseUpdates) {
442     Nullness leftVal = inputs.valueOfSubNode(leftNode);
443     Nullness rightVal = inputs.valueOfSubNode(rightNode);
444     Nullness equalBranchValue = leftVal.greatestLowerBound(rightVal);
445     Updates equalBranchUpdates = equalTo ? thenUpdates : elseUpdates;
446     Updates notEqualBranchUpdates = equalTo ? elseUpdates : thenUpdates;
447 
448     Node realLeftNode = unwrapAssignExpr(leftNode);
449     Node realRightNode = unwrapAssignExpr(rightNode);
450 
451     AccessPath leftAP = AccessPath.getAccessPathForNodeWithMapGet(realLeftNode, state, apContext);
452     if (leftAP != null) {
453       equalBranchUpdates.set(leftAP, equalBranchValue);
454       notEqualBranchUpdates.set(
455           leftAP, leftVal.greatestLowerBound(rightVal.deducedValueWhenNotEqual()));
456     }
457 
458     AccessPath rightAP = AccessPath.getAccessPathForNodeWithMapGet(realRightNode, state, apContext);
459     if (rightAP != null) {
460       equalBranchUpdates.set(rightAP, equalBranchValue);
461       notEqualBranchUpdates.set(
462           rightAP, rightVal.greatestLowerBound(leftVal.deducedValueWhenNotEqual()));
463     }
464   }
465 
466   @Override
visitConditionalAnd( ConditionalAndNode conditionalAndNode, TransferInput<Nullness, NullnessStore> input)467   public TransferResult<Nullness, NullnessStore> visitConditionalAnd(
468       ConditionalAndNode conditionalAndNode, TransferInput<Nullness, NullnessStore> input) {
469     return conditionalResult(input.getThenStore(), input.getElseStore(), NO_STORE_CHANGE);
470   }
471 
472   @Override
visitConditionalOr( ConditionalOrNode conditionalOrNode, TransferInput<Nullness, NullnessStore> input)473   public TransferResult<Nullness, NullnessStore> visitConditionalOr(
474       ConditionalOrNode conditionalOrNode, TransferInput<Nullness, NullnessStore> input) {
475     return conditionalResult(input.getThenStore(), input.getElseStore(), NO_STORE_CHANGE);
476   }
477 
478   @Override
visitConditionalNot( ConditionalNotNode conditionalNotNode, TransferInput<Nullness, NullnessStore> input)479   public TransferResult<Nullness, NullnessStore> visitConditionalNot(
480       ConditionalNotNode conditionalNotNode, TransferInput<Nullness, NullnessStore> input) {
481     boolean storeChanged = !input.getThenStore().equals(input.getElseStore());
482     return conditionalResult(
483         /* thenStore= */ input.getElseStore(), /* elseStore= */ input.getThenStore(), storeChanged);
484   }
485 
486   @Override
visitTernaryExpression( TernaryExpressionNode node, TransferInput<Nullness, NullnessStore> input)487   public TransferResult<Nullness, NullnessStore> visitTernaryExpression(
488       TernaryExpressionNode node, TransferInput<Nullness, NullnessStore> input) {
489     SubNodeValues inputs = values(input);
490     Nullness result =
491         inputs
492             .valueOfSubNode(node.getThenOperand())
493             .leastUpperBound(inputs.valueOfSubNode(node.getElseOperand()));
494     return new RegularTransferResult<>(result, input.getRegularStore());
495   }
496 
497   @Override
visitSwitchExpressionNode( SwitchExpressionNode node, TransferInput<Nullness, NullnessStore> input)498   public TransferResult<Nullness, NullnessStore> visitSwitchExpressionNode(
499       SwitchExpressionNode node, TransferInput<Nullness, NullnessStore> input) {
500     // The cfg includes assignments of the value of each case body of the switch expression
501     // to the switch expression var (a synthetic local variable).  So, the dataflow result
502     // for the switch expression is just the result for the switch expression var
503     return visitLocalVariable(node.getSwitchExpressionVar(), input);
504   }
505 
506   @Override
visitAssignment( AssignmentNode node, TransferInput<Nullness, NullnessStore> input)507   public TransferResult<Nullness, NullnessStore> visitAssignment(
508       AssignmentNode node, TransferInput<Nullness, NullnessStore> input) {
509     ReadableUpdates updates = new ReadableUpdates();
510     Node rhs = node.getExpression();
511     Nullness value = values(input).valueOfSubNode(rhs);
512     Node target = node.getTarget();
513 
514     if (target instanceof LocalVariableNode
515         && !ASTHelpers.getType(target.getTree()).isPrimitive()) {
516       LocalVariableNode localVariableNode = (LocalVariableNode) target;
517       updates.set(localVariableNode, value);
518       handleEnhancedForOverKeySet(localVariableNode, rhs, input, updates);
519     }
520 
521     if (target instanceof ArrayAccessNode) {
522       setNonnullIfAnalyzeable(updates, ((ArrayAccessNode) target).getArray());
523     }
524 
525     if (target instanceof FieldAccessNode) {
526       // we don't allow arbitrary access paths to be tracked from assignments
527       // here we still require an access of a field of this, or a static field
528       FieldAccessNode fieldAccessNode = (FieldAccessNode) target;
529       Node receiver = fieldAccessNode.getReceiver();
530       setNonnullIfAnalyzeable(updates, receiver);
531       if ((receiver instanceof ThisNode || fieldAccessNode.isStatic())
532           && fieldAccessNode.getElement().getKind().equals(ElementKind.FIELD)
533           && !ASTHelpers.getType(target.getTree()).isPrimitive()) {
534         updates.set(fieldAccessNode, value);
535       }
536     }
537 
538     return updateRegularStore(value, input, updates);
539   }
540 
541   /**
542    * Propagates access paths to track iteration over a map's key set using an enhanced-for loop,
543    * i.e., code of the form {@code for (Object k: m.keySet()) ...}. For such code, we track access
544    * paths to enable reasoning that within the body of the loop, {@code m.get(k)} is non-null.
545    *
546    * <p>There are two relevant types of assignments in the Checker Framework CFG for such tracking:
547    *
548    * <ol>
549    *   <li>{@code iter#numX = m.keySet().iterator()}, for getting the iterator over a key set for an
550    *       enhanced-for loop. After such assignments, we track an access path indicating that {@code
551    *       m.get(contentsOf(iter#numX)} is non-null.
552    *   <li>{@code k = iter#numX.next()}, which gets the next key in the key set when {@code
553    *       iter#numX} was assigned as in case 1. After such assignments, we track the desired {@code
554    *       m.get(k)} access path.
555    * </ol>
556    */
handleEnhancedForOverKeySet( LocalVariableNode lhs, Node rhs, TransferInput<Nullness, NullnessStore> input, ReadableUpdates updates)557   private void handleEnhancedForOverKeySet(
558       LocalVariableNode lhs,
559       Node rhs,
560       TransferInput<Nullness, NullnessStore> input,
561       ReadableUpdates updates) {
562     if (isEnhancedForIteratorVariable(lhs)) {
563       // Based on the structure of Checker Framework CFGs, rhs must be a call of the form
564       // e.iterator().  We check if e is a call to keySet() on a Map, and if so, propagate
565       // NONNULL for an access path for e.get(iteratorContents(lhs))
566       MethodInvocationNode rhsInv = (MethodInvocationNode) rhs;
567       Node mapNode = getMapNodeForKeySetIteratorCall(rhsInv);
568       if (mapNode != null) {
569         AccessPath mapWithIteratorContentsKey =
570             AccessPath.mapWithIteratorContentsKey(mapNode, lhs, apContext);
571         if (mapWithIteratorContentsKey != null) {
572           // put sanity check here to minimize perf impact
573           if (!isCallToMethod(rhsInv, SET_TYPE_SUPPLIER, "iterator")) {
574             throw new RuntimeException(
575                 "expected call to iterator(), instead saw "
576                     + state.getSourceForNode(rhsInv.getTree()));
577           }
578           updates.set(mapWithIteratorContentsKey, NONNULL);
579         }
580       }
581     } else if (rhs instanceof MethodInvocationNode) {
582       // Check for an assignment lhs = iter#numX.next().  From the structure of Checker Framework
583       // CFGs, we know that if iter#numX is the receiver of a call on the rhs of an assignment, it
584       // must be a call to next().
585       MethodInvocationNode methodInv = (MethodInvocationNode) rhs;
586       Node receiver = methodInv.getTarget().getReceiver();
587       if (receiver instanceof LocalVariableNode
588           && isEnhancedForIteratorVariable((LocalVariableNode) receiver)) {
589         // See if we are tracking an access path e.get(iteratorContents(receiver)).  If so, since
590         // lhs is being assigned from the iterator contents, propagate NONNULL for an access path
591         // e.get(lhs)
592         AccessPath mapGetPath =
593             input
594                 .getRegularStore()
595                 .getMapGetIteratorContentsAccessPath((LocalVariableNode) receiver);
596         if (mapGetPath != null) {
597           // put sanity check here to minimize perf impact
598           if (!isCallToMethod(methodInv, ITERATOR_TYPE_SUPPLIER, "next")) {
599             throw new RuntimeException(
600                 "expected call to next(), instead saw "
601                     + state.getSourceForNode(methodInv.getTree()));
602           }
603           updates.set(AccessPath.replaceMapKey(mapGetPath, AccessPath.fromLocal(lhs)), NONNULL);
604         }
605       }
606     }
607   }
608 
609   /**
610    * {@code invocationNode} must represent a call of the form {@code e.iterator()}. If {@code e} is
611    * of the form {@code e'.keySet()}, returns the {@code Node} for {@code e'}. Otherwise, returns
612    * {@code null}.
613    */
614   @Nullable
getMapNodeForKeySetIteratorCall(MethodInvocationNode invocationNode)615   private Node getMapNodeForKeySetIteratorCall(MethodInvocationNode invocationNode) {
616     Node receiver = invocationNode.getTarget().getReceiver();
617     if (receiver instanceof MethodInvocationNode) {
618       MethodInvocationNode baseInvocation = (MethodInvocationNode) receiver;
619       // Check for a call to java.util.Map.keySet()
620       if (NullabilityUtil.isMapMethod(
621           ASTHelpers.getSymbol(baseInvocation.getTree()), state, "keySet", 0)) {
622         // receiver represents the map
623         return baseInvocation.getTarget().getReceiver();
624       }
625     }
626     return null;
627   }
628 
isCallToMethod( MethodInvocationNode invocationNode, Supplier<Type> containingTypeSupplier, String methodName)629   private boolean isCallToMethod(
630       MethodInvocationNode invocationNode,
631       Supplier<Type> containingTypeSupplier,
632       String methodName) {
633     Symbol.MethodSymbol symbol = ASTHelpers.getSymbol(invocationNode.getTree());
634     return symbol != null
635         && symbol.getSimpleName().contentEquals(methodName)
636         && ASTHelpers.isSubtype(symbol.owner.type, containingTypeSupplier.get(state), state);
637   }
638 
639   /**
640    * Is {@code varNode} a temporary variable representing the {@code Iterator} for an enhanced for
641    * loop? Matched based on the naming scheme used by Checker dataflow.
642    */
isEnhancedForIteratorVariable(LocalVariableNode varNode)643   private boolean isEnhancedForIteratorVariable(LocalVariableNode varNode) {
644     return varNode.getName().startsWith("iter#num");
645   }
646 
updateRegularStore( Nullness value, TransferInput<Nullness, NullnessStore> input, ReadableUpdates updates)647   private TransferResult<Nullness, NullnessStore> updateRegularStore(
648       Nullness value, TransferInput<Nullness, NullnessStore> input, ReadableUpdates updates) {
649     ResultingStore newStore = updateStore(input.getRegularStore(), updates);
650     return new RegularTransferResult<>(value, newStore.store, newStore.storeChanged);
651   }
652 
653   /**
654    * If node represents a local, field access, or method call we can track, set it to be non-null in
655    * the updates
656    */
setNonnullIfAnalyzeable(Updates updates, Node node)657   private void setNonnullIfAnalyzeable(Updates updates, Node node) {
658     AccessPath ap = AccessPath.getAccessPathForNodeWithMapGet(node, state, apContext);
659     if (ap != null) {
660       updates.set(ap, NONNULL);
661     }
662   }
663 
hasPrimitiveType(Node node)664   private static boolean hasPrimitiveType(Node node) {
665     return node.getType().getKind().isPrimitive();
666   }
667 
hasNonNullConstantValue(LocalVariableNode node)668   private static boolean hasNonNullConstantValue(LocalVariableNode node) {
669     if (node.getElement() instanceof VariableElement) {
670       VariableElement element = (VariableElement) node.getElement();
671       return (element.getConstantValue() != null);
672     }
673     return false;
674   }
675 
676   @Override
visitLocalVariable( LocalVariableNode node, TransferInput<Nullness, NullnessStore> input)677   public TransferResult<Nullness, NullnessStore> visitLocalVariable(
678       LocalVariableNode node, TransferInput<Nullness, NullnessStore> input) {
679     NullnessStore values = input.getRegularStore();
680     Nullness nullness =
681         hasPrimitiveType(node) || hasNonNullConstantValue(node)
682             ? NONNULL
683             : values.valueOfLocalVariable(node, defaultAssumption);
684     return new RegularTransferResult<>(nullness, values);
685   }
686 
isCatchVariable(VariableDeclarationNode node)687   private static boolean isCatchVariable(VariableDeclarationNode node) {
688     return elementFromDeclaration(node.getTree()).getKind() == EXCEPTION_PARAMETER;
689   }
690 
691   @Override
visitVariableDeclaration( VariableDeclarationNode node, TransferInput<Nullness, NullnessStore> input)692   public TransferResult<Nullness, NullnessStore> visitVariableDeclaration(
693       VariableDeclarationNode node, TransferInput<Nullness, NullnessStore> input) {
694     ReadableUpdates updates = new ReadableUpdates();
695     if (isCatchVariable(node)) {
696       updates.set(node, NONNULL);
697     }
698     /*
699      * We can return whatever we want here because a variable declaration is not an expression and
700      * thus no one can use its value directly. Any updates to the nullness of the variable are
701      * performed in the store so that they are available to future reads.
702      */
703     return updateRegularStore(BOTTOM, input, updates);
704   }
705 
706   @Override
visitFieldAccess( FieldAccessNode fieldAccessNode, TransferInput<Nullness, NullnessStore> input)707   public TransferResult<Nullness, NullnessStore> visitFieldAccess(
708       FieldAccessNode fieldAccessNode, TransferInput<Nullness, NullnessStore> input) {
709     ReadableUpdates updates = new ReadableUpdates();
710     Symbol symbol = ASTHelpers.getSymbol(fieldAccessNode.getTree());
711     setReceiverNonnull(updates, fieldAccessNode.getReceiver(), symbol);
712     Nullness nullness = NULLABLE;
713     if (!NullabilityUtil.mayBeNullFieldFromType(symbol, config)) {
714       nullness = NONNULL;
715     } else {
716       nullness = input.getRegularStore().valueOfField(fieldAccessNode, nullness, apContext);
717     }
718     return updateRegularStore(nullness, input, updates);
719   }
720 
setReceiverNonnull( AccessPathNullnessPropagation.ReadableUpdates updates, Node receiver, Symbol symbol)721   private void setReceiverNonnull(
722       AccessPathNullnessPropagation.ReadableUpdates updates, Node receiver, Symbol symbol) {
723     if (symbol != null && !symbol.isStatic()) {
724       setNonnullIfAnalyzeable(updates, receiver);
725     }
726   }
727 
728   @Override
visitMethodAccess( MethodAccessNode methodAccessNode, TransferInput<Nullness, NullnessStore> input)729   public TransferResult<Nullness, NullnessStore> visitMethodAccess(
730       MethodAccessNode methodAccessNode, TransferInput<Nullness, NullnessStore> input) {
731     return noStoreChanges(NULLABLE, input);
732   }
733 
734   @Override
visitArrayAccess( ArrayAccessNode node, TransferInput<Nullness, NullnessStore> input)735   public TransferResult<Nullness, NullnessStore> visitArrayAccess(
736       ArrayAccessNode node, TransferInput<Nullness, NullnessStore> input) {
737     ReadableUpdates updates = new ReadableUpdates();
738     setNonnullIfAnalyzeable(updates, node.getArray());
739     // this is unsound
740     return updateRegularStore(defaultAssumption, input, updates);
741   }
742 
743   @Override
visitImplicitThis( ImplicitThisNode implicitThisNode, TransferInput<Nullness, NullnessStore> input)744   public TransferResult<Nullness, NullnessStore> visitImplicitThis(
745       ImplicitThisNode implicitThisNode, TransferInput<Nullness, NullnessStore> input) {
746     return noStoreChanges(NONNULL, input);
747   }
748 
749   @Override
visitExplicitThis( ExplicitThisNode explicitThisLiteralNode, TransferInput<Nullness, NullnessStore> input)750   public TransferResult<Nullness, NullnessStore> visitExplicitThis(
751       ExplicitThisNode explicitThisLiteralNode, TransferInput<Nullness, NullnessStore> input) {
752     return noStoreChanges(NONNULL, input);
753   }
754 
noStoreChanges( Nullness value, TransferInput<Nullness, NullnessStore> input)755   private TransferResult<Nullness, NullnessStore> noStoreChanges(
756       Nullness value, TransferInput<Nullness, NullnessStore> input) {
757     return new RegularTransferResult<>(value, input.getRegularStore());
758   }
759 
760   @Override
visitSuper( SuperNode superNode, TransferInput<Nullness, NullnessStore> input)761   public TransferResult<Nullness, NullnessStore> visitSuper(
762       SuperNode superNode, TransferInput<Nullness, NullnessStore> input) {
763     return noStoreChanges(NONNULL, input);
764   }
765 
766   @Override
visitReturn( ReturnNode returnNode, TransferInput<Nullness, NullnessStore> input)767   public TransferResult<Nullness, NullnessStore> visitReturn(
768       ReturnNode returnNode, TransferInput<Nullness, NullnessStore> input) {
769     handler.onDataflowVisitReturn(returnNode.getTree(), input.getThenStore(), input.getElseStore());
770     return noStoreChanges(NULLABLE, input);
771   }
772 
773   @Override
visitLambdaResultExpression( LambdaResultExpressionNode resultNode, TransferInput<Nullness, NullnessStore> input)774   public TransferResult<Nullness, NullnessStore> visitLambdaResultExpression(
775       LambdaResultExpressionNode resultNode, TransferInput<Nullness, NullnessStore> input) {
776     handler.onDataflowVisitLambdaResultExpression(
777         resultNode.getTree(), input.getThenStore(), input.getElseStore());
778     SubNodeValues values = values(input);
779     Nullness nullness = values.valueOfSubNode(resultNode.getResult());
780     return noStoreChanges(nullness, input);
781   }
782 
783   @Override
visitStringConversion( StringConversionNode stringConversionNode, TransferInput<Nullness, NullnessStore> input)784   public TransferResult<Nullness, NullnessStore> visitStringConversion(
785       StringConversionNode stringConversionNode, TransferInput<Nullness, NullnessStore> input) {
786     return noStoreChanges(NONNULL, input);
787   }
788 
789   @Override
visitNarrowingConversion( NarrowingConversionNode narrowingConversionNode, TransferInput<Nullness, NullnessStore> input)790   public TransferResult<Nullness, NullnessStore> visitNarrowingConversion(
791       NarrowingConversionNode narrowingConversionNode,
792       TransferInput<Nullness, NullnessStore> input) {
793     return noStoreChanges(NONNULL, input);
794   }
795 
796   @Override
visitWideningConversion( WideningConversionNode wideningConversionNode, TransferInput<Nullness, NullnessStore> input)797   public TransferResult<Nullness, NullnessStore> visitWideningConversion(
798       WideningConversionNode wideningConversionNode, TransferInput<Nullness, NullnessStore> input) {
799     return noStoreChanges(NONNULL, input);
800   }
801 
802   @Override
visitInstanceOf( InstanceOfNode node, TransferInput<Nullness, NullnessStore> input)803   public TransferResult<Nullness, NullnessStore> visitInstanceOf(
804       InstanceOfNode node, TransferInput<Nullness, NullnessStore> input) {
805     ReadableUpdates thenUpdates = new ReadableUpdates();
806     ReadableUpdates elseUpdates = new ReadableUpdates();
807     setNonnullIfAnalyzeable(thenUpdates, node.getOperand());
808     ResultingStore thenStore = updateStore(input.getThenStore(), thenUpdates);
809     ResultingStore elseStore = updateStore(input.getElseStore(), elseUpdates);
810     return new ConditionalTransferResult<>(
811         NONNULL,
812         thenStore.store,
813         elseStore.store,
814         thenStore.storeChanged || elseStore.storeChanged);
815   }
816 
817   @Override
visitTypeCast( TypeCastNode node, TransferInput<Nullness, NullnessStore> input)818   public TransferResult<Nullness, NullnessStore> visitTypeCast(
819       TypeCastNode node, TransferInput<Nullness, NullnessStore> input) {
820     SubNodeValues values = values(input);
821     Nullness nullness = hasPrimitiveType(node) ? NONNULL : values.valueOfSubNode(node.getOperand());
822     return noStoreChanges(nullness, input);
823   }
824 
825   @Override
visitSynchronized( SynchronizedNode synchronizedNode, TransferInput<Nullness, NullnessStore> input)826   public TransferResult<Nullness, NullnessStore> visitSynchronized(
827       SynchronizedNode synchronizedNode, TransferInput<Nullness, NullnessStore> input) {
828     return noStoreChanges(NULLABLE, input);
829   }
830 
831   @Override
visitAssertionError( AssertionErrorNode assertionErrorNode, TransferInput<Nullness, NullnessStore> input)832   public TransferResult<Nullness, NullnessStore> visitAssertionError(
833       AssertionErrorNode assertionErrorNode, TransferInput<Nullness, NullnessStore> input) {
834 
835     Node condition = assertionErrorNode.getCondition();
836 
837     if (condition == null
838         || !(condition instanceof NotEqualNode)
839         || !(((NotEqualNode) condition).getRightOperand() instanceof NullLiteralNode)) {
840       return noStoreChanges(NULLABLE, input);
841     }
842 
843     AccessPath accessPath =
844         AccessPath.getAccessPathForNodeNoMapGet(
845             ((NotEqualNode) condition).getLeftOperand(), apContext);
846 
847     if (accessPath == null) {
848       return noStoreChanges(NULLABLE, input);
849     }
850 
851     ReadableUpdates updates = new ReadableUpdates();
852     updates.set(accessPath, NONNULL);
853 
854     return updateRegularStore(NULLABLE, input, updates);
855   }
856 
857   @Override
visitThrow( ThrowNode throwNode, TransferInput<Nullness, NullnessStore> input)858   public TransferResult<Nullness, NullnessStore> visitThrow(
859       ThrowNode throwNode, TransferInput<Nullness, NullnessStore> input) {
860     return noStoreChanges(NULLABLE, input);
861   }
862 
863   @Override
visitCase( CaseNode caseNode, TransferInput<Nullness, NullnessStore> input)864   public TransferResult<Nullness, NullnessStore> visitCase(
865       CaseNode caseNode, TransferInput<Nullness, NullnessStore> input) {
866     return noStoreChanges(NULLABLE, input);
867   }
868 
869   @Override
visitMethodInvocation( MethodInvocationNode node, TransferInput<Nullness, NullnessStore> input)870   public TransferResult<Nullness, NullnessStore> visitMethodInvocation(
871       MethodInvocationNode node, TransferInput<Nullness, NullnessStore> input) {
872     ReadableUpdates thenUpdates = new ReadableUpdates();
873     ReadableUpdates elseUpdates = new ReadableUpdates();
874     ReadableUpdates bothUpdates = new ReadableUpdates();
875     Symbol.MethodSymbol callee = ASTHelpers.getSymbol(node.getTree());
876     Preconditions.checkNotNull(callee);
877     setReceiverNonnull(bothUpdates, node.getTarget().getReceiver(), callee);
878     setNullnessForMapCalls(
879         node, callee, node.getArguments(), values(input), thenUpdates, bothUpdates);
880     NullnessHint nullnessHint =
881         handler.onDataflowVisitMethodInvocation(
882             node,
883             state.getTypes(),
884             state.context,
885             apContext,
886             values(input),
887             thenUpdates,
888             elseUpdates,
889             bothUpdates);
890     Nullness nullness = returnValueNullness(node, input, nullnessHint);
891     if (booleanReturnType(node)) {
892       ResultingStore thenStore = updateStore(input.getThenStore(), thenUpdates, bothUpdates);
893       ResultingStore elseStore = updateStore(input.getElseStore(), elseUpdates, bothUpdates);
894       return conditionalResult(
895           thenStore.store, elseStore.store, thenStore.storeChanged || elseStore.storeChanged);
896     }
897     return updateRegularStore(nullness, input, bothUpdates);
898   }
899 
setNullnessForMapCalls( MethodInvocationNode node, Symbol.MethodSymbol callee, List<Node> arguments, AccessPathNullnessPropagation.SubNodeValues inputs, AccessPathNullnessPropagation.Updates thenUpdates, AccessPathNullnessPropagation.Updates bothUpdates)900   private void setNullnessForMapCalls(
901       MethodInvocationNode node,
902       Symbol.MethodSymbol callee,
903       List<Node> arguments,
904       AccessPathNullnessPropagation.SubNodeValues inputs,
905       AccessPathNullnessPropagation.Updates thenUpdates,
906       AccessPathNullnessPropagation.Updates bothUpdates) {
907     if (AccessPath.isContainsKey(callee, state)) {
908       // make sure argument is a variable, and get its element
909       AccessPath getAccessPath = AccessPath.getForMapInvocation(node, apContext);
910       if (getAccessPath != null) {
911         // in the then branch, we want the get() call with the same argument to be non-null
912         // we assume that the declared target of the get() method will be in the same class
913         // as containsKey()
914         thenUpdates.set(getAccessPath, NONNULL);
915       }
916     } else if (AccessPath.isMapPut(callee, state)) {
917       AccessPath getAccessPath = AccessPath.getForMapInvocation(node, apContext);
918       if (getAccessPath != null) {
919         Nullness value = inputs.valueOfSubNode(arguments.get(1));
920         bothUpdates.set(getAccessPath, value);
921       }
922     }
923   }
924 
booleanReturnType(MethodInvocationNode node)925   private boolean booleanReturnType(MethodInvocationNode node) {
926     Symbol.MethodSymbol methodSymbol = ASTHelpers.getSymbol(node.getTree());
927     return methodSymbol != null && methodSymbol.getReturnType().getTag() == TypeTag.BOOLEAN;
928   }
929 
returnValueNullness( MethodInvocationNode node, TransferInput<Nullness, NullnessStore> input, NullnessHint returnValueNullnessHint)930   Nullness returnValueNullness(
931       MethodInvocationNode node,
932       TransferInput<Nullness, NullnessStore> input,
933       NullnessHint returnValueNullnessHint) {
934     // NULLABLE is our default
935     Nullness nullness;
936     if (node != null && returnValueNullnessHint == NullnessHint.FORCE_NONNULL) {
937       // A handler says this is definitely non-null; trust it. Note that FORCE_NONNULL is quite
938       // dangerous, since it
939       // ignores our analysis' own best judgement, so both this value and the annotations that cause
940       // it (e.g.
941       // @Contract ) should be used with care.
942       nullness = NONNULL;
943     } else if (node != null && returnValueNullnessHint == NullnessHint.HINT_NULLABLE) {
944       // we have a model saying return value is nullable.
945       // still, rely on dataflow fact if there is one available
946       nullness = input.getRegularStore().valueOfMethodCall(node, state, NULLABLE, apContext);
947     } else if (node == null
948         || methodReturnsNonNull.test(node)
949         || !Nullness.hasNullableAnnotation((Symbol) node.getTarget().getMethod(), config)) {
950       // definite non-null return
951       nullness = NONNULL;
952     } else {
953       // rely on dataflow, assuming nullable if no fact
954       nullness = input.getRegularStore().valueOfMethodCall(node, state, NULLABLE, apContext);
955     }
956     return nullness;
957   }
958 
959   @Override
visitObjectCreation( ObjectCreationNode objectCreationNode, TransferInput<Nullness, NullnessStore> input)960   public TransferResult<Nullness, NullnessStore> visitObjectCreation(
961       ObjectCreationNode objectCreationNode, TransferInput<Nullness, NullnessStore> input) {
962     return noStoreChanges(NONNULL, input);
963   }
964 
965   @Override
visitMemberReference( FunctionalInterfaceNode functionalInterfaceNode, TransferInput<Nullness, NullnessStore> input)966   public TransferResult<Nullness, NullnessStore> visitMemberReference(
967       FunctionalInterfaceNode functionalInterfaceNode,
968       TransferInput<Nullness, NullnessStore> input) {
969     return noStoreChanges(NONNULL, input);
970   }
971 
972   @Override
visitArrayCreation( ArrayCreationNode arrayCreationNode, TransferInput<Nullness, NullnessStore> input)973   public TransferResult<Nullness, NullnessStore> visitArrayCreation(
974       ArrayCreationNode arrayCreationNode, TransferInput<Nullness, NullnessStore> input) {
975     return noStoreChanges(NONNULL, input);
976   }
977 
978   @Override
visitArrayType( ArrayTypeNode arrayTypeNode, TransferInput<Nullness, NullnessStore> input)979   public TransferResult<Nullness, NullnessStore> visitArrayType(
980       ArrayTypeNode arrayTypeNode, TransferInput<Nullness, NullnessStore> input) {
981     return noStoreChanges(NULLABLE, input);
982   }
983 
984   @Override
visitPrimitiveType( PrimitiveTypeNode primitiveTypeNode, TransferInput<Nullness, NullnessStore> input)985   public TransferResult<Nullness, NullnessStore> visitPrimitiveType(
986       PrimitiveTypeNode primitiveTypeNode, TransferInput<Nullness, NullnessStore> input) {
987     return noStoreChanges(NULLABLE, input);
988   }
989 
990   @Override
visitClassName( ClassNameNode classNameNode, TransferInput<Nullness, NullnessStore> input)991   public TransferResult<Nullness, NullnessStore> visitClassName(
992       ClassNameNode classNameNode, TransferInput<Nullness, NullnessStore> input) {
993     return noStoreChanges(NULLABLE, input);
994   }
995 
996   @Override
visitClassDeclaration( ClassDeclarationNode classDeclarationNode, TransferInput<Nullness, NullnessStore> input)997   public TransferResult<Nullness, NullnessStore> visitClassDeclaration(
998       ClassDeclarationNode classDeclarationNode, TransferInput<Nullness, NullnessStore> input) {
999     return noStoreChanges(NULLABLE, input);
1000   }
1001 
1002   @Override
visitPackageName( PackageNameNode packageNameNode, TransferInput<Nullness, NullnessStore> input)1003   public TransferResult<Nullness, NullnessStore> visitPackageName(
1004       PackageNameNode packageNameNode, TransferInput<Nullness, NullnessStore> input) {
1005     return noStoreChanges(NULLABLE, input);
1006   }
1007 
1008   @Override
visitParameterizedType( ParameterizedTypeNode parameterizedTypeNode, TransferInput<Nullness, NullnessStore> input)1009   public TransferResult<Nullness, NullnessStore> visitParameterizedType(
1010       ParameterizedTypeNode parameterizedTypeNode, TransferInput<Nullness, NullnessStore> input) {
1011     return noStoreChanges(NULLABLE, input);
1012   }
1013 
1014   @Override
visitMarker( MarkerNode markerNode, TransferInput<Nullness, NullnessStore> input)1015   public TransferResult<Nullness, NullnessStore> visitMarker(
1016       MarkerNode markerNode, TransferInput<Nullness, NullnessStore> input) {
1017     return noStoreChanges(NULLABLE, input);
1018   }
1019 
1020   @CheckReturnValue
updateStore(NullnessStore oldStore, ReadableUpdates... updates)1021   private static ResultingStore updateStore(NullnessStore oldStore, ReadableUpdates... updates) {
1022     NullnessStore.Builder builder = oldStore.toBuilder();
1023     for (ReadableUpdates update : updates) {
1024       for (Map.Entry<AccessPath, Nullness> entry : update.values.entrySet()) {
1025         AccessPath key = entry.getKey();
1026         builder.setInformation(key, entry.getValue());
1027       }
1028     }
1029     NullnessStore newStore = builder.build();
1030     return new ResultingStore(newStore, !newStore.equals(oldStore));
1031   }
1032 
conditionalResult( NullnessStore thenStore, NullnessStore elseStore, boolean storeChanged)1033   private static TransferResult<Nullness, NullnessStore> conditionalResult(
1034       NullnessStore thenStore, NullnessStore elseStore, boolean storeChanged) {
1035     return new ConditionalTransferResult<>(NONNULL, thenStore, elseStore, storeChanged);
1036   }
1037 
1038   /**
1039    * Provides the previously computed nullness values of descendant nodes. All descendant nodes have
1040    * already been assigned a value, if only the default of {@code NULLABLE}.
1041    */
1042   public interface SubNodeValues {
valueOfSubNode(Node node)1043     public Nullness valueOfSubNode(Node node);
1044   }
1045 
1046   private static final class ResultingStore {
1047     final NullnessStore store;
1048     final boolean storeChanged;
1049 
ResultingStore(NullnessStore store, boolean storeChanged)1050     ResultingStore(NullnessStore store, boolean storeChanged) {
1051       this.store = store;
1052       this.storeChanged = storeChanged;
1053     }
1054   }
1055 
1056   /** Represents a set of updates to be applied to the NullnessStore. */
1057   public interface Updates {
1058 
set(LocalVariableNode node, Nullness value)1059     void set(LocalVariableNode node, Nullness value);
1060 
set(VariableDeclarationNode node, Nullness value)1061     void set(VariableDeclarationNode node, Nullness value);
1062 
set(FieldAccessNode node, Nullness value)1063     void set(FieldAccessNode node, Nullness value);
1064 
set(MethodInvocationNode node, Nullness value)1065     void set(MethodInvocationNode node, Nullness value);
1066 
set(AccessPath ap, Nullness value)1067     void set(AccessPath ap, Nullness value);
1068   }
1069 
1070   private final class ReadableUpdates implements Updates {
1071     final Map<AccessPath, Nullness> values = new HashMap<>();
1072 
1073     @Override
set(LocalVariableNode node, Nullness value)1074     public void set(LocalVariableNode node, Nullness value) {
1075       values.put(AccessPath.fromLocal(node), checkNotNull(value));
1076     }
1077 
1078     @Override
set(VariableDeclarationNode node, Nullness value)1079     public void set(VariableDeclarationNode node, Nullness value) {
1080       values.put(AccessPath.fromVarDecl(node), checkNotNull(value));
1081     }
1082 
1083     @Override
set(FieldAccessNode node, Nullness value)1084     public void set(FieldAccessNode node, Nullness value) {
1085       AccessPath accessPath = AccessPath.fromFieldAccess(node, apContext);
1086       values.put(Preconditions.checkNotNull(accessPath), checkNotNull(value));
1087     }
1088 
1089     @Override
set(MethodInvocationNode node, Nullness value)1090     public void set(MethodInvocationNode node, Nullness value) {
1091       AccessPath path = AccessPath.fromMethodCall(node, state, apContext);
1092       values.put(checkNotNull(path), value);
1093     }
1094 
1095     @Override
set(AccessPath ap, Nullness value)1096     public void set(AccessPath ap, Nullness value) {
1097       values.put(checkNotNull(ap), value);
1098     }
1099   }
1100 }
1101