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