• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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