1 /* 2 * Copyright (c) 2017 Uber Technologies, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * of this software and associated documentation files (the "Software"), to deal 6 * in the Software without restriction, including without limitation the rights 7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 * copies of the Software, and to permit persons to whom the Software is 9 * furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 * THE SOFTWARE. 21 */ 22 23 package com.uber.nullaway.handlers; 24 25 import com.google.common.collect.ImmutableSet; 26 import com.google.errorprone.VisitorState; 27 import com.sun.source.tree.ClassTree; 28 import com.sun.source.tree.ExpressionTree; 29 import com.sun.source.tree.LambdaExpressionTree; 30 import com.sun.source.tree.MemberReferenceTree; 31 import com.sun.source.tree.MethodInvocationTree; 32 import com.sun.source.tree.MethodTree; 33 import com.sun.source.tree.ReturnTree; 34 import com.sun.tools.javac.code.Symbol; 35 import com.sun.tools.javac.code.Types; 36 import com.sun.tools.javac.util.Context; 37 import com.uber.nullaway.ErrorMessage; 38 import com.uber.nullaway.NullAway; 39 import com.uber.nullaway.Nullness; 40 import com.uber.nullaway.dataflow.AccessPath; 41 import com.uber.nullaway.dataflow.AccessPathNullnessPropagation; 42 import com.uber.nullaway.dataflow.NullnessStore; 43 import java.util.List; 44 import java.util.Optional; 45 import org.checkerframework.nullaway.dataflow.cfg.UnderlyingAST; 46 import org.checkerframework.nullaway.dataflow.cfg.node.LocalVariableNode; 47 import org.checkerframework.nullaway.dataflow.cfg.node.MethodInvocationNode; 48 49 /** 50 * The general interface representing a handler. 51 * 52 * <p>Handlers are used to model specific libraries and APIs, as opposed to general features of the 53 * Java language, which are handled by the nullability checker core and dataflow packages. 54 */ 55 public interface Handler { 56 57 /** 58 * Called when NullAway matches a particular top level class. 59 * 60 * <p>This also means we are starting a new Compilation Unit, which allows us to clear CU-specific 61 * state. 62 * 63 * @param analysis A reference to the running NullAway analysis. 64 * @param tree The AST node for the class being matched. 65 * @param state The current visitor state. 66 * @param classSymbol The class symbol for the class being matched. 67 */ onMatchTopLevelClass( NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol)68 void onMatchTopLevelClass( 69 NullAway analysis, ClassTree tree, VisitorState state, Symbol.ClassSymbol classSymbol); 70 71 /** 72 * Called when NullAway first matches a particular method node. 73 * 74 * @param analysis A reference to the running NullAway analysis. 75 * @param tree The AST node for the method being matched. 76 * @param state The current visitor state. 77 * @param methodSymbol The method symbol for the method being matched. 78 */ onMatchMethod( NullAway analysis, MethodTree tree, VisitorState state, Symbol.MethodSymbol methodSymbol)79 void onMatchMethod( 80 NullAway analysis, MethodTree tree, VisitorState state, Symbol.MethodSymbol methodSymbol); 81 82 /** 83 * Called when NullAway first matches a particular method call-site. 84 * 85 * @param analysis A reference to the running NullAway analysis. 86 * @param tree The AST node for the method invocation (call-site) being matched. 87 * @param state The current visitor state. 88 * @param methodSymbol The method symbol for the method being called. 89 */ onMatchMethodInvocation( NullAway analysis, MethodInvocationTree tree, VisitorState state, Symbol.MethodSymbol methodSymbol)90 void onMatchMethodInvocation( 91 NullAway analysis, 92 MethodInvocationTree tree, 93 VisitorState state, 94 Symbol.MethodSymbol methodSymbol); 95 96 /** 97 * Called when NullAway first matches a particular lambda expression. 98 * 99 * @param analysis A reference to the running NullAway analysis. 100 * @param tree The AST node for the lambda expression being matched. 101 * @param state The current visitor state. 102 * @param methodSymbol The method symbol for the functional interface of the lambda being matched. 103 */ onMatchLambdaExpression( NullAway analysis, LambdaExpressionTree tree, VisitorState state, Symbol.MethodSymbol methodSymbol)104 void onMatchLambdaExpression( 105 NullAway analysis, 106 LambdaExpressionTree tree, 107 VisitorState state, 108 Symbol.MethodSymbol methodSymbol); 109 110 /** 111 * Called when NullAway first matches a particular method reference expression 112 * 113 * @param analysis A reference to the running NullAway analysis. 114 * @param tree The AST node for the method reference expression being matched. 115 * @param state The current visitor state. 116 * @param methodSymbol The method symbol for the reference being matched. 117 */ onMatchMethodReference( NullAway analysis, MemberReferenceTree tree, VisitorState state, Symbol.MethodSymbol methodSymbol)118 void onMatchMethodReference( 119 NullAway analysis, 120 MemberReferenceTree tree, 121 VisitorState state, 122 Symbol.MethodSymbol methodSymbol); 123 124 /** 125 * Called when NullAway first matches a return statement. 126 * 127 * @param analysis A reference to the running NullAway analysis. 128 * @param tree The AST node for the return statement being matched. 129 * @param state The current visitor state. 130 */ onMatchReturn(NullAway analysis, ReturnTree tree, VisitorState state)131 void onMatchReturn(NullAway analysis, ReturnTree tree, VisitorState state); 132 133 /** 134 * Called when NullAway encounters an unannotated method and asks for params explicitly 135 * marked @Nullable 136 * 137 * <p>This is mostly used to force overriding methods to also use @Nullable, e.g. for callbacks 138 * that can get passed null values. 139 * 140 * <p>Note that this should be only used for parameters EXPLICLTY marked as @Nullable (e.g. by 141 * library models) rather than those considered @Nullable by NullAway's default optimistic 142 * assumptions. 143 * 144 * @param methodSymbol The method symbol for the unannotated method in question. 145 * @param explicitlyNullablePositions Parameter nullability computed by upstream handlers (the 146 * core analysis supplies the empty set to the first handler in the chain). 147 * @return Updated parameter nullability computed by this handler. 148 */ onUnannotatedInvocationGetExplicitlyNullablePositions( Context context, Symbol.MethodSymbol methodSymbol, ImmutableSet<Integer> explicitlyNullablePositions)149 ImmutableSet<Integer> onUnannotatedInvocationGetExplicitlyNullablePositions( 150 Context context, 151 Symbol.MethodSymbol methodSymbol, 152 ImmutableSet<Integer> explicitlyNullablePositions); 153 154 /** 155 * Called when NullAway encounters an unannotated method and asks if return value is explicitly 156 * marked @Nullable 157 * 158 * <p>Note that this should be only used for return values EXPLICLTY marked as @NonNull (e.g. by 159 * library models) rather than those considered @Nullable by NullAway's default optimistic 160 * assumptions. 161 * 162 * @param methodSymbol The method symbol for the unannotated method in question. 163 * @param explicitlyNonNullReturn return nullability computed by upstream handlers. 164 * @return Updated return nullability computed by this handler. 165 */ onUnannotatedInvocationGetExplicitlyNonNullReturn( Symbol.MethodSymbol methodSymbol, boolean explicitlyNonNullReturn)166 boolean onUnannotatedInvocationGetExplicitlyNonNullReturn( 167 Symbol.MethodSymbol methodSymbol, boolean explicitlyNonNullReturn); 168 169 /** 170 * Called when NullAway encounters an unannotated method and asks for params default nullability 171 * 172 * @param analysis A reference to the running NullAway analysis. 173 * @param state The current visitor state. 174 * @param methodSymbol The method symbol for the unannotated method in question. 175 * @param actualParams The actual parameters from the invocation node 176 * @param nonNullPositions Parameter nullability computed by upstream handlers (the core analysis 177 * supplies the empty set to the first handler in the chain). 178 * @return Updated parameter nullability computed by this handler. 179 */ onUnannotatedInvocationGetNonNullPositions( NullAway analysis, VisitorState state, Symbol.MethodSymbol methodSymbol, List<? extends ExpressionTree> actualParams, ImmutableSet<Integer> nonNullPositions)180 ImmutableSet<Integer> onUnannotatedInvocationGetNonNullPositions( 181 NullAway analysis, 182 VisitorState state, 183 Symbol.MethodSymbol methodSymbol, 184 List<? extends ExpressionTree> actualParams, 185 ImmutableSet<Integer> nonNullPositions); 186 187 /** 188 * Called after the analysis determines if a expression can be null or not, allowing handlers to 189 * override. 190 * 191 * @param analysis A reference to the running NullAway analysis. 192 * @param expr The expression in question. 193 * @param state The current visitor state. 194 * @param exprMayBeNull Whether or not the expression may be null according to the base analysis 195 * or upstream handlers. 196 * @return Whether or not the expression may be null, as updated by this handler. 197 */ onOverrideMayBeNullExpr( NullAway analysis, ExpressionTree expr, VisitorState state, boolean exprMayBeNull)198 boolean onOverrideMayBeNullExpr( 199 NullAway analysis, ExpressionTree expr, VisitorState state, boolean exprMayBeNull); 200 201 /** 202 * Called when the Dataflow analysis generates the initial NullnessStore for a method or lambda. 203 * 204 * @param underlyingAST The AST node for the method's (or lambda's) body, using the checkers 205 * framework UnderlyingAST class. 206 * @param parameters The formal parameters of the method. 207 * @param result The state of the initial NullnessStore for the method (or lambda) at the point 208 * this hook is called, represented as a builder. 209 * @return The desired state of the initial NullnessStore for the method (or lambda), after this 210 * hook is called, represented as a builder. Usually, implementors of this hook will either 211 * take {@code result} and call {@code setInformation(...)} on it to add additional nullness 212 * facts, or replace it with a new builder altogether. 213 */ onDataflowInitialStore( UnderlyingAST underlyingAST, List<LocalVariableNode> parameters, NullnessStore.Builder result)214 NullnessStore.Builder onDataflowInitialStore( 215 UnderlyingAST underlyingAST, 216 List<LocalVariableNode> parameters, 217 NullnessStore.Builder result); 218 219 /** 220 * Called when the Dataflow analysis visits each method invocation. 221 * 222 * @param node The AST node for the method callsite. 223 * @param types {@link Types} for the current compilation 224 * @param apContext the current access path context information (see {@link 225 * AccessPath.AccessPathContext}). 226 * @param inputs NullnessStore information known before the method invocation. 227 * @param thenUpdates NullnessStore updates to be added along the then path, handlers can add via 228 * the set() method. 229 * @param elseUpdates NullnessStore updates to be added along the else path, handlers can add via 230 * the set() method. 231 * @param bothUpdates NullnessStore updates to be added along both paths, handlers can add via the 232 * set() method. 233 * @return The Nullness information for this method's return computed by this handler. See 234 * NullnessHint and CompositeHandler for more information about how this values get merged 235 * into a final Nullness value. 236 */ onDataflowVisitMethodInvocation( MethodInvocationNode node, Types types, Context context, AccessPath.AccessPathContext apContext, AccessPathNullnessPropagation.SubNodeValues inputs, AccessPathNullnessPropagation.Updates thenUpdates, AccessPathNullnessPropagation.Updates elseUpdates, AccessPathNullnessPropagation.Updates bothUpdates)237 NullnessHint onDataflowVisitMethodInvocation( 238 MethodInvocationNode node, 239 Types types, 240 Context context, 241 AccessPath.AccessPathContext apContext, 242 AccessPathNullnessPropagation.SubNodeValues inputs, 243 AccessPathNullnessPropagation.Updates thenUpdates, 244 AccessPathNullnessPropagation.Updates elseUpdates, 245 AccessPathNullnessPropagation.Updates bothUpdates); 246 247 /** 248 * Called when the Dataflow analysis visits a return statement. 249 * 250 * @param tree The AST node for the return statement being matched. 251 * @param thenStore The NullnessStore for the true case of the expression inside the return 252 * statement. 253 * @param elseStore The NullnessStore for the false case of the expression inside the return 254 * statement. 255 */ onDataflowVisitReturn(ReturnTree tree, NullnessStore thenStore, NullnessStore elseStore)256 void onDataflowVisitReturn(ReturnTree tree, NullnessStore thenStore, NullnessStore elseStore); 257 258 /** 259 * Called when the Dataflow analysis visits the result expression inside the body of lambda. 260 * 261 * <p>This is only called for lambda expressions with a single expression as their body. For 262 * lambdas with a block of code as their body, onDataflowVisitReturn will be called instead, one 263 * or more times. 264 * 265 * <p>It is not expected to be called for anything other than boolean expressions, which are the 266 * only ones for which providing separate then/else stores makes sense. For simply getting the 267 * final exit store of the lambda, see Dataflow.finalResult or 268 * AccessPathNullnessAnalysis.forceRunOnMethod. 269 * 270 * @param tree The AST node for the expression being matched. 271 * @param thenStore The NullnessStore for the true case of the expression inside the return 272 * statement. 273 * @param elseStore The NullnessStore for the false case of the expression inside the return 274 * statement. 275 */ onDataflowVisitLambdaResultExpression( ExpressionTree tree, NullnessStore thenStore, NullnessStore elseStore)276 void onDataflowVisitLambdaResultExpression( 277 ExpressionTree tree, NullnessStore thenStore, NullnessStore elseStore); 278 279 /** 280 * It should return an error wrapped in Optional if any of the handlers detect an error in 281 * dereference. 282 * 283 * @param expr The AST node for the expression being matched. 284 * @param baseExpr The AST node for the base of dereference expression being matched. 285 * @param state The current visitor state. 286 * @return {@link ErrorMessage} wrapped in {@link Optional} if dereference causes some error, 287 * otherwise returns empty Optional 288 */ onExpressionDereference( ExpressionTree expr, ExpressionTree baseExpr, VisitorState state)289 Optional<ErrorMessage> onExpressionDereference( 290 ExpressionTree expr, ExpressionTree baseExpr, VisitorState state); 291 292 /** 293 * Called when the store access paths are filtered for local variable information before an 294 * expression. 295 * 296 * @param accessPath The access path that needs to be checked if filtered. 297 * @param state The current visitor state. 298 * @return true if the nullability information for this accesspath should be treated as part of 299 * the surrounding context when processing a lambda expression or anonymous class declaration. 300 */ includeApInfoInSavedContext(AccessPath accessPath, VisitorState state)301 boolean includeApInfoInSavedContext(AccessPath accessPath, VisitorState state); 302 303 /** 304 * Called during dataflow analysis initialization to register structurally immutable types. 305 * 306 * <p>Handlers declare structurally immutable types, requesting that they be treated as constants 307 * when they appear as arguments of method inside an AccessPath. Whenever a static final field of 308 * one of these types appears as an argument to a method in an access path (e.g. get(Foo.f) where 309 * Foo.f is a static final field of an immutable type T returned by this method), it is treated 310 * the same as a String or primitive type compile-time constant for the purposes of tracking the 311 * nullability of that access path. 312 * 313 * @return A set of fully qualified immutable type names. 314 */ onRegisterImmutableTypes()315 ImmutableSet<String> onRegisterImmutableTypes(); 316 317 /** 318 * A three value enum for handlers implementing onDataflowVisitMethodInvocation to communicate 319 * their knowledge of the method return nullability to the the rest of NullAway. 320 */ 321 public enum NullnessHint { 322 /** 323 * No new information about return nullability, defer to the core algorithm and other handlers. 324 */ 325 UNKNOWN(0, Nullness.NONNULL), 326 /** 327 * The method is nullable in general (e.g. due to a library model or a return type annotation). 328 * Override core behavior and mark this method as Nullable, unless a handler returns 329 * FORCE_NONNULL. 330 */ 331 HINT_NULLABLE(1, Nullness.NULLABLE), 332 /** 333 * Handler asserts this method is guaranteed to be NonNull for the current invocation (e.g. used 334 * by ContractHandler when all preconditions for a "-> !null" clause are known to hold). 335 */ 336 FORCE_NONNULL(2, Nullness.NONNULL); 337 338 private final int priority; 339 private final Nullness nullness; 340 NullnessHint(int priority, Nullness nullness)341 NullnessHint(int priority, Nullness nullness) { 342 this.priority = priority; 343 this.nullness = nullness; 344 } 345 toNullness()346 public Nullness toNullness() { 347 return nullness; 348 } 349 merge(NullnessHint other)350 public NullnessHint merge(NullnessHint other) { 351 if (other.priority > this.priority) { 352 return other; 353 } else { 354 return this; 355 } 356 } 357 } 358 } 359