• 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;
24 
25 import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
26 import static com.sun.source.tree.Tree.Kind.EXPRESSION_STATEMENT;
27 import static com.sun.source.tree.Tree.Kind.IDENTIFIER;
28 import static com.sun.source.tree.Tree.Kind.OTHER;
29 import static com.sun.source.tree.Tree.Kind.PARENTHESIZED;
30 import static com.sun.source.tree.Tree.Kind.TYPE_CAST;
31 import static com.uber.nullaway.ErrorBuilder.errMsgForInitializer;
32 
33 import com.google.auto.service.AutoService;
34 import com.google.auto.value.AutoValue;
35 import com.google.common.base.Preconditions;
36 import com.google.common.collect.ImmutableList;
37 import com.google.common.collect.ImmutableMultimap;
38 import com.google.common.collect.ImmutableSet;
39 import com.google.common.collect.LinkedHashMultimap;
40 import com.google.common.collect.Multimap;
41 import com.google.common.collect.SetMultimap;
42 import com.google.common.collect.Sets;
43 import com.google.errorprone.BugPattern;
44 import com.google.errorprone.ErrorProneFlags;
45 import com.google.errorprone.VisitorState;
46 import com.google.errorprone.bugpatterns.BugChecker;
47 import com.google.errorprone.fixes.SuggestedFix;
48 import com.google.errorprone.matchers.Description;
49 import com.google.errorprone.matchers.Matcher;
50 import com.google.errorprone.matchers.Matchers;
51 import com.google.errorprone.suppliers.Suppliers;
52 import com.google.errorprone.util.ASTHelpers;
53 import com.sun.source.tree.AnnotationTree;
54 import com.sun.source.tree.ArrayAccessTree;
55 import com.sun.source.tree.AssignmentTree;
56 import com.sun.source.tree.BinaryTree;
57 import com.sun.source.tree.BlockTree;
58 import com.sun.source.tree.ClassTree;
59 import com.sun.source.tree.CompoundAssignmentTree;
60 import com.sun.source.tree.ConditionalExpressionTree;
61 import com.sun.source.tree.EnhancedForLoopTree;
62 import com.sun.source.tree.ExpressionStatementTree;
63 import com.sun.source.tree.ExpressionTree;
64 import com.sun.source.tree.ForLoopTree;
65 import com.sun.source.tree.IdentifierTree;
66 import com.sun.source.tree.IfTree;
67 import com.sun.source.tree.LambdaExpressionTree;
68 import com.sun.source.tree.MemberReferenceTree;
69 import com.sun.source.tree.MemberSelectTree;
70 import com.sun.source.tree.MethodInvocationTree;
71 import com.sun.source.tree.MethodTree;
72 import com.sun.source.tree.NewClassTree;
73 import com.sun.source.tree.ParenthesizedTree;
74 import com.sun.source.tree.ReturnTree;
75 import com.sun.source.tree.StatementTree;
76 import com.sun.source.tree.SwitchTree;
77 import com.sun.source.tree.Tree;
78 import com.sun.source.tree.TryTree;
79 import com.sun.source.tree.TypeCastTree;
80 import com.sun.source.tree.UnaryTree;
81 import com.sun.source.tree.VariableTree;
82 import com.sun.source.tree.WhileLoopTree;
83 import com.sun.source.util.TreePath;
84 import com.sun.source.util.Trees;
85 import com.sun.tools.javac.code.Symbol;
86 import com.sun.tools.javac.code.Symbol.VarSymbol;
87 import com.sun.tools.javac.code.Type;
88 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
89 import com.sun.tools.javac.tree.JCTree;
90 import com.uber.nullaway.ErrorMessage.MessageTypes;
91 import com.uber.nullaway.dataflow.AccessPathNullnessAnalysis;
92 import com.uber.nullaway.dataflow.EnclosingEnvironmentNullness;
93 import com.uber.nullaway.handlers.Handler;
94 import com.uber.nullaway.handlers.Handlers;
95 import java.util.ArrayList;
96 import java.util.LinkedHashMap;
97 import java.util.LinkedHashSet;
98 import java.util.List;
99 import java.util.Map;
100 import java.util.Optional;
101 import java.util.Set;
102 import java.util.function.Predicate;
103 import java.util.stream.Collectors;
104 import java.util.stream.StreamSupport;
105 import javax.annotation.Nullable;
106 import javax.lang.model.element.AnnotationMirror;
107 import javax.lang.model.element.Element;
108 import javax.lang.model.element.ElementKind;
109 import javax.lang.model.element.Modifier;
110 import javax.lang.model.element.NestingKind;
111 import javax.lang.model.type.TypeKind;
112 import org.checkerframework.nullaway.dataflow.cfg.node.MethodInvocationNode;
113 import org.checkerframework.nullaway.javacutil.ElementUtils;
114 import org.checkerframework.nullaway.javacutil.TreeUtils;
115 
116 /**
117  * Checker for nullability errors. It assumes that any field, method parameter, or return type that
118  * may be null is annotated with {@link Nullable}, and then checks the following rules:
119  *
120  * <ul>
121  *   <li>no assignment of a nullable expression into a non-null field
122  *   <li>no passing a nullable expression into a non-null parameter
123  *   <li>no returning a nullable expression from a method with non-null return type
124  *   <li>no field access or method invocation on an expression that is nullable
125  * </ul>
126  *
127  * <p>This checker also detects errors related to field initialization. For any @NonNull instance
128  * field <code>f</code>, this checker ensures that at least one of the following cases holds:
129  *
130  * <ol>
131  *   <li><code>f</code> is directly initialized at its declaration
132  *   <li><code>f</code> is always initialized in all constructors
133  *   <li><code>f</code> is always initialized in some method annotated with @Initializer
134  * </ol>
135  *
136  * <p>For any @NonNull static field <code>f</code>, this checker ensures that at least one of the
137  * following cases holds:
138  *
139  * <ol>
140  *   <li><code>f</code> is directly initialized at its declaration
141  *   <li><code>f</code> is always initialized in some static initializer block
142  * </ol>
143  */
144 @AutoService(BugChecker.class)
145 @BugPattern(
146     name = "NullAway",
147     altNames = {"CheckNullabilityTypes"},
148     summary = "Nullability type error.",
149     tags = BugPattern.StandardTags.LIKELY_ERROR,
150     severity = WARNING)
151 public class NullAway extends BugChecker
152     implements BugChecker.MethodInvocationTreeMatcher,
153         BugChecker.AssignmentTreeMatcher,
154         BugChecker.MemberSelectTreeMatcher,
155         BugChecker.ArrayAccessTreeMatcher,
156         BugChecker.ReturnTreeMatcher,
157         BugChecker.ClassTreeMatcher,
158         BugChecker.MethodTreeMatcher,
159         BugChecker.VariableTreeMatcher,
160         BugChecker.NewClassTreeMatcher,
161         BugChecker.BinaryTreeMatcher,
162         BugChecker.UnaryTreeMatcher,
163         BugChecker.ConditionalExpressionTreeMatcher,
164         BugChecker.IfTreeMatcher,
165         BugChecker.WhileLoopTreeMatcher,
166         BugChecker.ForLoopTreeMatcher,
167         BugChecker.EnhancedForLoopTreeMatcher,
168         BugChecker.LambdaExpressionTreeMatcher,
169         BugChecker.IdentifierTreeMatcher,
170         BugChecker.MemberReferenceTreeMatcher,
171         BugChecker.CompoundAssignmentTreeMatcher,
172         BugChecker.SwitchTreeMatcher {
173 
174   static final String INITIALIZATION_CHECK_NAME = "NullAway.Init";
175   static final String OPTIONAL_CHECK_NAME = "NullAway.Optional";
176   // Unmatched, used for when we only want full checker suppressions to work
177   static final String CORE_CHECK_NAME = "NullAway.<core>";
178 
179   private static final Matcher<ExpressionTree> THIS_MATCHER = NullAway::isThisIdentifierMatcher;
180 
181   private final Predicate<MethodInvocationNode> nonAnnotatedMethod;
182 
183   /** should we match within the current top level class? */
184   private boolean matchWithinTopLevelClass = true;
185 
186   private final Config config;
187 
188   private final ErrorBuilder errorBuilder;
189 
190   /**
191    * The handler passed to our analysis (usually a {@code CompositeHandler} including handlers for
192    * various APIs.
193    */
194   private final Handler handler;
195 
196   /**
197    * entities relevant to field initialization per class. cached for performance. nulled out in
198    * {@link #matchClass(ClassTree, VisitorState)}
199    */
200   private final Map<Symbol.ClassSymbol, FieldInitEntities> class2Entities = new LinkedHashMap<>();
201 
202   /**
203    * fields not initialized by constructors, per class. cached for performance. nulled out in {@link
204    * #matchClass(ClassTree, VisitorState)}
205    */
206   private final SetMultimap<Symbol.ClassSymbol, Symbol> class2ConstructorUninit =
207       LinkedHashMultimap.create();
208 
209   /**
210    * maps each top-level initialization member (constructor, init block, field decl with initializer
211    * expression) to the set of @NonNull fields known to be initialized before that member executes.
212    *
213    * <p>cached for performance. nulled out in {@link #matchClass(ClassTree, VisitorState)}
214    */
215   private final Map<Symbol.ClassSymbol, Multimap<Tree, Element>> initTree2PrevFieldInit =
216       new LinkedHashMap<>();
217 
218   /**
219    * dynamically computer/overriden nullness facts for certain expressions, such as specific method
220    * calls where we can infer a more precise set of facts than those given by the method's
221    * annotations.
222    */
223   private final Map<ExpressionTree, Nullness> computedNullnessMap = new LinkedHashMap<>();
224 
225   /**
226    * Used to check if a symbol represents a module in {@link #matchMemberSelect(MemberSelectTree,
227    * VisitorState)}. We need to use reflection to preserve compatibility with Java 8.
228    *
229    * <p>TODO remove this once NullAway requires JDK 11
230    */
231   @Nullable private final Class<?> moduleElementClass;
232 
233   /**
234    * Error Prone requires us to have an empty constructor for each Plugin, in addition to the
235    * constructor taking an ErrorProneFlags object. This constructor should not be used anywhere
236    * else. Checker objects constructed with this constructor will fail with IllegalStateException if
237    * ever used for analysis.
238    */
NullAway()239   public NullAway() {
240     config = new DummyOptionsConfig();
241     handler = Handlers.buildEmpty();
242     nonAnnotatedMethod = this::isMethodUnannotated;
243     errorBuilder = new ErrorBuilder(config, "", ImmutableSet.of());
244     moduleElementClass = null;
245   }
246 
NullAway(ErrorProneFlags flags)247   public NullAway(ErrorProneFlags flags) {
248     config = new ErrorProneCLIFlagsConfig(flags);
249     handler = Handlers.buildDefault(config);
250     nonAnnotatedMethod = this::isMethodUnannotated;
251     errorBuilder = new ErrorBuilder(config, canonicalName(), allNames());
252     Class<?> moduleElementClass = null;
253     try {
254       moduleElementClass =
255           getClass().getClassLoader().loadClass("javax.lang.model.element.ModuleElement");
256     } catch (ClassNotFoundException e) {
257       // can occur pre JDK 11
258     }
259     this.moduleElementClass = moduleElementClass;
260   }
261 
isMethodUnannotated(MethodInvocationNode invocationNode)262   private boolean isMethodUnannotated(MethodInvocationNode invocationNode) {
263     return invocationNode == null
264         || NullabilityUtil.isUnannotated(ASTHelpers.getSymbol(invocationNode.getTree()), config);
265   }
266 
267   @Override
linkUrl()268   public String linkUrl() {
269     // add a space to make it clickable from iTerm
270     return config.getErrorURL() + " ";
271   }
272 
273   /**
274    * We are trying to see if (1) we are in a method guaranteed to return something non-null, and (2)
275    * this return statement can return something null.
276    */
277   @Override
matchReturn(ReturnTree tree, VisitorState state)278   public Description matchReturn(ReturnTree tree, VisitorState state) {
279     if (!matchWithinTopLevelClass) {
280       return Description.NO_MATCH;
281     }
282     handler.onMatchReturn(this, tree, state);
283     ExpressionTree retExpr = tree.getExpression();
284     // let's do quick checks on returned expression first
285     if (retExpr == null) {
286       return Description.NO_MATCH;
287     }
288     // now let's check the enclosing method
289     TreePath enclosingMethodOrLambda =
290         NullabilityUtil.findEnclosingMethodOrLambdaOrInitializer(state.getPath());
291     if (enclosingMethodOrLambda == null) {
292       throw new RuntimeException("no enclosing method, lambda or initializer!");
293     }
294     if (!(enclosingMethodOrLambda.getLeaf() instanceof MethodTree
295         || enclosingMethodOrLambda.getLeaf() instanceof LambdaExpressionTree)) {
296       throw new RuntimeException(
297           "return statement outside of a method or lambda! (e.g. in an initializer block)");
298     }
299     Tree leaf = enclosingMethodOrLambda.getLeaf();
300     Symbol.MethodSymbol methodSymbol;
301     if (leaf instanceof MethodTree) {
302       MethodTree enclosingMethod = (MethodTree) leaf;
303       methodSymbol = ASTHelpers.getSymbol(enclosingMethod);
304     } else {
305       // we have a lambda
306       methodSymbol =
307           NullabilityUtil.getFunctionalInterfaceMethod(
308               (LambdaExpressionTree) leaf, state.getTypes());
309     }
310     return checkReturnExpression(tree, retExpr, methodSymbol, state);
311   }
312 
313   @Override
matchMethodInvocation(MethodInvocationTree tree, VisitorState state)314   public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
315     if (!matchWithinTopLevelClass) {
316       return Description.NO_MATCH;
317     }
318     final Symbol.MethodSymbol methodSymbol = ASTHelpers.getSymbol(tree);
319     if (methodSymbol == null) {
320       throw new RuntimeException("not expecting unresolved method here");
321     }
322     handler.onMatchMethodInvocation(this, tree, state, methodSymbol);
323     // assuming this list does not include the receiver
324     List<? extends ExpressionTree> actualParams = tree.getArguments();
325     return handleInvocation(tree, state, methodSymbol, actualParams);
326   }
327 
328   @Override
matchNewClass(NewClassTree tree, VisitorState state)329   public Description matchNewClass(NewClassTree tree, VisitorState state) {
330     if (!matchWithinTopLevelClass) {
331       return Description.NO_MATCH;
332     }
333     Symbol.MethodSymbol methodSymbol = ASTHelpers.getSymbol(tree);
334     if (methodSymbol == null) {
335       throw new RuntimeException("not expecting unresolved method here");
336     }
337     List<? extends ExpressionTree> actualParams = tree.getArguments();
338     if (tree.getClassBody() != null && actualParams.size() > 0) {
339       // passing parameters to constructor of anonymous class
340       // this constructor just invokes the constructor of the superclass, and
341       // in the AST does not have the parameter nullability annotations from the superclass.
342       // so, treat as if the superclass constructor is being invoked directly
343       // see https://github.com/uber/NullAway/issues/102
344       methodSymbol = getSymbolOfSuperConstructor(methodSymbol, state);
345     }
346     return handleInvocation(tree, state, methodSymbol, actualParams);
347   }
348 
349   /**
350    * Updates the {@link EnclosingEnvironmentNullness} with an entry for lambda or anonymous class,
351    * capturing nullability info for locals just before the declaration of the entity
352    *
353    * @param tree either a lambda or a local / anonymous class
354    * @param state visitor state
355    */
updateEnvironmentMapping(Tree tree, VisitorState state)356   private void updateEnvironmentMapping(Tree tree, VisitorState state) {
357     AccessPathNullnessAnalysis analysis = getNullnessAnalysis(state);
358     // two notes:
359     // 1. we are free to take local variable information from the program point before
360     // the lambda / class declaration as only effectively final variables can be accessed
361     // from the nested scope, so the program point doesn't matter
362     // 2. we keep info on all locals rather than just effectively final ones for simplicity
363     EnclosingEnvironmentNullness.instance(state.context)
364         .addEnvironmentMapping(
365             tree, analysis.getNullnessInfoBeforeNewContext(state.getPath(), state, handler));
366   }
367 
getSymbolOfSuperConstructor( Symbol.MethodSymbol anonClassConstructorSymbol, VisitorState state)368   private Symbol.MethodSymbol getSymbolOfSuperConstructor(
369       Symbol.MethodSymbol anonClassConstructorSymbol, VisitorState state) {
370     // get the statements in the body of the anonymous class constructor
371     List<? extends StatementTree> statements =
372         getTreesInstance(state).getTree(anonClassConstructorSymbol).getBody().getStatements();
373     // there should be exactly one statement, which is an invocation of the super constructor
374     if (statements.size() == 1) {
375       StatementTree stmt = statements.get(0);
376       if (stmt instanceof ExpressionStatementTree) {
377         ExpressionTree expression = ((ExpressionStatementTree) stmt).getExpression();
378         if (expression instanceof MethodInvocationTree) {
379           return ASTHelpers.getSymbol((MethodInvocationTree) expression);
380         }
381       }
382     }
383     throw new IllegalStateException("unexpected anonymous class constructor body " + statements);
384   }
385 
386   @Override
matchAssignment(AssignmentTree tree, VisitorState state)387   public Description matchAssignment(AssignmentTree tree, VisitorState state) {
388     if (!matchWithinTopLevelClass) {
389       return Description.NO_MATCH;
390     }
391     Type lhsType = ASTHelpers.getType(tree.getVariable());
392     if (lhsType != null && lhsType.isPrimitive()) {
393       return doUnboxingCheck(state, tree.getExpression());
394     }
395     Symbol assigned = ASTHelpers.getSymbol(tree.getVariable());
396     if (assigned == null || assigned.getKind() != ElementKind.FIELD) {
397       // not a field of nullable type
398       return Description.NO_MATCH;
399     }
400 
401     if (Nullness.hasNullableAnnotation(assigned, config)) {
402       // field already annotated
403       return Description.NO_MATCH;
404     }
405     ExpressionTree expression = tree.getExpression();
406     if (mayBeNullExpr(state, expression)) {
407       String message = "assigning @Nullable expression to @NonNull field";
408       return errorBuilder.createErrorDescriptionForNullAssignment(
409           new ErrorMessage(MessageTypes.ASSIGN_FIELD_NULLABLE, message),
410           expression,
411           buildDescription(tree),
412           state);
413     }
414     return Description.NO_MATCH;
415   }
416 
417   @Override
matchCompoundAssignment(CompoundAssignmentTree tree, VisitorState state)418   public Description matchCompoundAssignment(CompoundAssignmentTree tree, VisitorState state) {
419     if (!matchWithinTopLevelClass) {
420       return Description.NO_MATCH;
421     }
422     Type lhsType = ASTHelpers.getType(tree.getVariable());
423     Type stringType = Suppliers.STRING_TYPE.get(state);
424     if (lhsType != null && !state.getTypes().isSameType(lhsType, stringType)) {
425       // both LHS and RHS could get unboxed
426       return doUnboxingCheck(state, tree.getVariable(), tree.getExpression());
427     }
428     return Description.NO_MATCH;
429   }
430 
431   @Override
matchArrayAccess(ArrayAccessTree tree, VisitorState state)432   public Description matchArrayAccess(ArrayAccessTree tree, VisitorState state) {
433     if (!matchWithinTopLevelClass) {
434       return Description.NO_MATCH;
435     }
436     Description description = matchDereference(tree.getExpression(), tree, state);
437     if (!description.equals(Description.NO_MATCH)) {
438       return description;
439     }
440     // also check for unboxing of array index expression
441     return doUnboxingCheck(state, tree.getIndex());
442   }
443 
444   @Override
matchMemberSelect(MemberSelectTree tree, VisitorState state)445   public Description matchMemberSelect(MemberSelectTree tree, VisitorState state) {
446     if (!matchWithinTopLevelClass) {
447       return Description.NO_MATCH;
448     }
449     Symbol symbol = ASTHelpers.getSymbol(tree);
450     // Some checks for cases where we know this cannot be a null dereference.  The tree's symbol may
451     // be null in cases where the tree represents part of a package name, e.g., in the package
452     // declaration in a class, or in a requires clause in a module-info.java file; it should never
453     // be null for a real field dereference or method call
454     if (symbol == null
455         || symbol.getSimpleName().toString().equals("class")
456         || symbol.isEnum()
457         || isModuleSymbol(symbol)) {
458       return Description.NO_MATCH;
459     }
460 
461     Description badDeref = matchDereference(tree.getExpression(), tree, state);
462     if (!badDeref.equals(Description.NO_MATCH)) {
463       return badDeref;
464     }
465     // if we're accessing a field of this, make sure we're not reading the field before init
466     if (tree.getExpression() instanceof IdentifierTree
467         && ((IdentifierTree) tree.getExpression()).getName().toString().equals("this")) {
468       return checkForReadBeforeInit(tree, state);
469     }
470     return Description.NO_MATCH;
471   }
472 
473   /**
474    * Checks if {@code symbol} represents a JDK 9+ module using reflection.
475    *
476    * <p>TODO just check using instanceof once NullAway requires JDK 11
477    */
isModuleSymbol(Symbol symbol)478   private boolean isModuleSymbol(Symbol symbol) {
479     return moduleElementClass != null && moduleElementClass.isAssignableFrom(symbol.getClass());
480   }
481 
482   @Override
matchMethod(MethodTree tree, VisitorState state)483   public Description matchMethod(MethodTree tree, VisitorState state) {
484     if (!matchWithinTopLevelClass) {
485       return Description.NO_MATCH;
486     }
487     // if the method is overriding some other method,
488     // check that nullability annotations are consistent with
489     // overridden method (if overridden method is in an annotated
490     // package)
491     Symbol.MethodSymbol methodSymbol = ASTHelpers.getSymbol(tree);
492     handler.onMatchMethod(this, tree, state, methodSymbol);
493     boolean isOverriding = ASTHelpers.hasAnnotation(methodSymbol, Override.class, state);
494     boolean exhaustiveOverride = config.exhaustiveOverride();
495     if (isOverriding || !exhaustiveOverride) {
496       Symbol.MethodSymbol closestOverriddenMethod =
497           NullabilityUtil.getClosestOverriddenMethod(methodSymbol, state.getTypes());
498       if (closestOverriddenMethod != null) {
499         return checkOverriding(closestOverriddenMethod, methodSymbol, null, state);
500       }
501     }
502     return Description.NO_MATCH;
503   }
504 
505   @Override
matchSwitch(SwitchTree tree, VisitorState state)506   public Description matchSwitch(SwitchTree tree, VisitorState state) {
507     if (!matchWithinTopLevelClass) {
508       return Description.NO_MATCH;
509     }
510 
511     ExpressionTree switchSelectorExpression = tree.getExpression();
512     // For a statement `switch (e) { ... }`, javac returns `(e)` as the selector expression.  We
513     // strip the outermost parentheses for a nicer-looking error message.
514     if (switchSelectorExpression instanceof ParenthesizedTree) {
515       switchSelectorExpression = ((ParenthesizedTree) switchSelectorExpression).getExpression();
516     }
517 
518     if (mayBeNullExpr(state, switchSelectorExpression)) {
519       final String message =
520           "switch expression " + state.getSourceForNode(switchSelectorExpression) + " is @Nullable";
521       ErrorMessage errorMessage =
522           new ErrorMessage(MessageTypes.SWITCH_EXPRESSION_NULLABLE, message);
523 
524       return errorBuilder.createErrorDescription(
525           errorMessage,
526           switchSelectorExpression,
527           buildDescription(switchSelectorExpression),
528           state);
529     }
530 
531     return Description.NO_MATCH;
532   }
533 
534   /**
535    * checks that an overriding method does not override a {@code @Nullable} parameter with a
536    * {@code @NonNull} parameter
537    *
538    * @param overridingParamSymbols parameters of the overriding method
539    * @param overriddenMethod method being overridden
540    * @param lambdaExpressionTree if the overriding method is a lambda, the {@link
541    *     LambdaExpressionTree}; otherwise {@code null}
542    * @param memberReferenceTree if the overriding method is a member reference (which "overrides" a
543    *     functional interface method), the {@link MemberReferenceTree}; otherwise {@code null}
544    * @return discovered error, or {@link Description#NO_MATCH} if no error
545    */
checkParamOverriding( List<VarSymbol> overridingParamSymbols, Symbol.MethodSymbol overriddenMethod, @Nullable LambdaExpressionTree lambdaExpressionTree, @Nullable MemberReferenceTree memberReferenceTree, VisitorState state)546   private Description checkParamOverriding(
547       List<VarSymbol> overridingParamSymbols,
548       Symbol.MethodSymbol overriddenMethod,
549       @Nullable LambdaExpressionTree lambdaExpressionTree,
550       @Nullable MemberReferenceTree memberReferenceTree,
551       VisitorState state) {
552     com.sun.tools.javac.util.List<VarSymbol> superParamSymbols = overriddenMethod.getParameters();
553     boolean unboundMemberRef =
554         (memberReferenceTree != null)
555             && ((JCTree.JCMemberReference) memberReferenceTree).kind.isUnbound();
556     // if we have an unbound method reference, the first parameter of the overridden method must be
557     // @NonNull, as this parameter will be used as a method receiver inside the generated lambda
558     if (unboundMemberRef) {
559       boolean isFirstParamNull = false;
560       // Two cases: for annotated code, look first at the annotation
561       if (!NullabilityUtil.isUnannotated(overriddenMethod, config)) {
562         isFirstParamNull = Nullness.hasNullableAnnotation(superParamSymbols.get(0), config);
563       }
564       // For both annotated and unannotated code, look then at handler overrides (e.g. Library
565       // Models)
566       isFirstParamNull =
567           handler
568               .onUnannotatedInvocationGetExplicitlyNullablePositions(
569                   state.context,
570                   overriddenMethod,
571                   isFirstParamNull ? ImmutableSet.of(0) : ImmutableSet.of())
572               .contains(0);
573       if (isFirstParamNull) {
574         String message =
575             "unbound instance method reference cannot be used, as first parameter of "
576                 + "functional interface method "
577                 + ASTHelpers.enclosingClass(overriddenMethod)
578                 + "."
579                 + overriddenMethod.toString()
580                 + " is @Nullable";
581         return errorBuilder.createErrorDescription(
582             new ErrorMessage(MessageTypes.WRONG_OVERRIDE_PARAM, message),
583             buildDescription(memberReferenceTree),
584             state);
585       }
586     }
587     // for unbound member references, we need to adjust parameter indices by 1 when matching with
588     // overridden method
589     int startParam = unboundMemberRef ? 1 : 0;
590     // Collect @Nullable params of overriden method
591     ImmutableSet<Integer> nullableParamsOfOverriden;
592     if (NullabilityUtil.isUnannotated(overriddenMethod, config)) {
593       nullableParamsOfOverriden =
594           handler.onUnannotatedInvocationGetExplicitlyNullablePositions(
595               state.context, overriddenMethod, ImmutableSet.of());
596     } else {
597       ImmutableSet.Builder<Integer> builder = ImmutableSet.builder();
598       for (int i = startParam; i < superParamSymbols.size(); i++) {
599         // we need to call paramHasNullableAnnotation here since overriddenMethod may be defined
600         // in a class file
601         if (Nullness.paramHasNullableAnnotation(overriddenMethod, i, config)) {
602           builder.add(i);
603         }
604       }
605       nullableParamsOfOverriden = builder.build();
606     }
607     for (int i : nullableParamsOfOverriden) {
608       int methodParamInd = i - startParam;
609       VarSymbol paramSymbol = overridingParamSymbols.get(methodParamInd);
610       // in the case where we have a parameter of a lambda expression, we do
611       // *not* force the parameter to be annotated with @Nullable; instead we "inherit"
612       // nullability from the corresponding functional interface method.
613       // So, we report an error if the @Nullable annotation is missing *and*
614       // we don't have a lambda with implicitly typed parameters
615       boolean implicitlyTypedLambdaParam =
616           lambdaExpressionTree != null
617               && NullabilityUtil.lambdaParamIsImplicitlyTyped(
618                   lambdaExpressionTree.getParameters().get(methodParamInd));
619       if (!Nullness.hasNullableAnnotation(paramSymbol, config) && !implicitlyTypedLambdaParam) {
620         final String message =
621             "parameter "
622                 + paramSymbol.name.toString()
623                 + (memberReferenceTree != null ? " of referenced method" : "")
624                 + " is @NonNull, but parameter in "
625                 + ((lambdaExpressionTree != null || memberReferenceTree != null)
626                     ? "functional interface "
627                     : "superclass ")
628                 + "method "
629                 + ASTHelpers.enclosingClass(overriddenMethod)
630                 + "."
631                 + overriddenMethod.toString()
632                 + " is @Nullable";
633         Tree errorTree;
634         if (memberReferenceTree != null) {
635           errorTree = memberReferenceTree;
636         } else {
637           errorTree = getTreesInstance(state).getTree(paramSymbol);
638         }
639         return errorBuilder.createErrorDescription(
640             new ErrorMessage(MessageTypes.WRONG_OVERRIDE_PARAM, message),
641             buildDescription(errorTree),
642             state);
643       }
644     }
645     return Description.NO_MATCH;
646   }
647 
getTreesInstance(VisitorState state)648   static Trees getTreesInstance(VisitorState state) {
649     return Trees.instance(JavacProcessingEnvironment.instance(state.context));
650   }
651 
checkReturnExpression( Tree tree, ExpressionTree retExpr, Symbol.MethodSymbol methodSymbol, VisitorState state)652   private Description checkReturnExpression(
653       Tree tree, ExpressionTree retExpr, Symbol.MethodSymbol methodSymbol, VisitorState state) {
654     Type returnType = methodSymbol.getReturnType();
655     if (returnType.isPrimitive()) {
656       // check for unboxing
657       return doUnboxingCheck(state, retExpr);
658     }
659     if (returnType.toString().equals("java.lang.Void")) {
660       return Description.NO_MATCH;
661     }
662     if (NullabilityUtil.isUnannotated(methodSymbol, config)
663         || Nullness.hasNullableAnnotation(methodSymbol, config)) {
664       return Description.NO_MATCH;
665     }
666     if (mayBeNullExpr(state, retExpr)) {
667       final ErrorMessage errorMessage =
668           new ErrorMessage(
669               MessageTypes.RETURN_NULLABLE,
670               "returning @Nullable expression from method with @NonNull return type");
671 
672       return errorBuilder.createErrorDescriptionForNullAssignment(
673           errorMessage, retExpr, buildDescription(tree), state);
674     }
675     return Description.NO_MATCH;
676   }
677 
678   @Override
matchLambdaExpression(LambdaExpressionTree tree, VisitorState state)679   public Description matchLambdaExpression(LambdaExpressionTree tree, VisitorState state) {
680     if (!matchWithinTopLevelClass) {
681       return Description.NO_MATCH;
682     }
683     Symbol.MethodSymbol funcInterfaceMethod =
684         NullabilityUtil.getFunctionalInterfaceMethod(tree, state.getTypes());
685     // we need to update environment mapping before running the handler, as some handlers
686     // (like Rx nullability) run dataflow analysis
687     updateEnvironmentMapping(tree, state);
688     handler.onMatchLambdaExpression(this, tree, state, funcInterfaceMethod);
689     if (NullabilityUtil.isUnannotated(funcInterfaceMethod, config)) {
690       return Description.NO_MATCH;
691     }
692     Description description =
693         checkParamOverriding(
694             tree.getParameters().stream().map(ASTHelpers::getSymbol).collect(Collectors.toList()),
695             funcInterfaceMethod,
696             tree,
697             null,
698             state);
699     if (description != Description.NO_MATCH) {
700       return description;
701     }
702     // if the body has a return statement, that gets checked in matchReturn().  We need this code
703     // for lambdas with expression bodies
704     if (tree.getBodyKind() == LambdaExpressionTree.BodyKind.EXPRESSION
705         && funcInterfaceMethod.getReturnType().getKind() != TypeKind.VOID) {
706       ExpressionTree resExpr = (ExpressionTree) tree.getBody();
707       return checkReturnExpression(tree, resExpr, funcInterfaceMethod, state);
708     }
709     return Description.NO_MATCH;
710   }
711 
712   /**
713    * for method references, we check that the referenced method correctly overrides the
714    * corresponding functional interface method
715    */
716   @Override
matchMemberReference(MemberReferenceTree tree, VisitorState state)717   public Description matchMemberReference(MemberReferenceTree tree, VisitorState state) {
718     if (!matchWithinTopLevelClass) {
719       return Description.NO_MATCH;
720     }
721     Symbol.MethodSymbol referencedMethod = ASTHelpers.getSymbol(tree);
722     Symbol.MethodSymbol funcInterfaceSymbol =
723         NullabilityUtil.getFunctionalInterfaceMethod(tree, state.getTypes());
724     handler.onMatchMethodReference(this, tree, state, referencedMethod);
725     return checkOverriding(funcInterfaceSymbol, referencedMethod, tree, state);
726   }
727 
728   /**
729    * check that nullability annotations of an overriding method are consistent with those in the
730    * overridden method (both return and parameters)
731    *
732    * @param overriddenMethod method being overridden
733    * @param overridingMethod overriding method
734    * @param memberReferenceTree if override is via a method reference, the relevant {@link
735    *     MemberReferenceTree}; otherwise {@code null}. If non-null, overridingTree is the AST of the
736    *     referenced method
737    * @param state visitor state.
738    * @return discovered error, or {@link Description#NO_MATCH} if no error
739    */
checkOverriding( Symbol.MethodSymbol overriddenMethod, Symbol.MethodSymbol overridingMethod, @Nullable MemberReferenceTree memberReferenceTree, VisitorState state)740   private Description checkOverriding(
741       Symbol.MethodSymbol overriddenMethod,
742       Symbol.MethodSymbol overridingMethod,
743       @Nullable MemberReferenceTree memberReferenceTree,
744       VisitorState state) {
745     final boolean isOverridenMethodUnannotated =
746         NullabilityUtil.isUnannotated(overriddenMethod, config);
747     final boolean overriddenMethodReturnsNonNull =
748         ((isOverridenMethodUnannotated
749                 && handler.onUnannotatedInvocationGetExplicitlyNonNullReturn(
750                     overriddenMethod, false))
751             || (!isOverridenMethodUnannotated
752                 && !Nullness.hasNullableAnnotation(overriddenMethod, config)));
753     // if the super method returns nonnull,
754     // overriding method better not return nullable
755     if (overriddenMethodReturnsNonNull
756         && Nullness.hasNullableAnnotation(overridingMethod, config)
757         && getComputedNullness(memberReferenceTree).equals(Nullness.NULLABLE)) {
758       String message;
759       if (memberReferenceTree != null) {
760         message =
761             "referenced method returns @Nullable, but functional interface method "
762                 + ASTHelpers.enclosingClass(overriddenMethod)
763                 + "."
764                 + overriddenMethod.toString()
765                 + " returns @NonNull";
766 
767       } else {
768         message =
769             "method returns @Nullable, but superclass method "
770                 + ASTHelpers.enclosingClass(overriddenMethod)
771                 + "."
772                 + overriddenMethod.toString()
773                 + " returns @NonNull";
774       }
775       Tree errorTree =
776           memberReferenceTree != null
777               ? memberReferenceTree
778               : getTreesInstance(state).getTree(overridingMethod);
779       return errorBuilder.createErrorDescription(
780           new ErrorMessage(MessageTypes.WRONG_OVERRIDE_RETURN, message),
781           buildDescription(errorTree),
782           state);
783     }
784     // if any parameter in the super method is annotated @Nullable,
785     // overriding method cannot assume @Nonnull
786     return checkParamOverriding(
787         overridingMethod.getParameters(), overriddenMethod, null, memberReferenceTree, state);
788   }
789 
790   @Override
matchIdentifier(IdentifierTree tree, VisitorState state)791   public Description matchIdentifier(IdentifierTree tree, VisitorState state) {
792     if (!matchWithinTopLevelClass) {
793       return Description.NO_MATCH;
794     }
795     return checkForReadBeforeInit(tree, state);
796   }
797 
checkForReadBeforeInit(ExpressionTree tree, VisitorState state)798   private Description checkForReadBeforeInit(ExpressionTree tree, VisitorState state) {
799     // do a bunch of filtering.  first, filter out anything outside an initializer
800     TreePath path = state.getPath();
801     TreePath enclosingBlockPath;
802     if (config.assertsEnabled()) {
803       enclosingBlockPath = NullabilityUtil.findEnclosingMethodOrLambdaOrInitializer(path);
804     } else {
805       enclosingBlockPath =
806           NullabilityUtil.findEnclosingMethodOrLambdaOrInitializer(
807               path, ImmutableSet.of(Tree.Kind.ASSERT));
808     }
809     if (enclosingBlockPath == null) {
810       // is this possible?
811       return Description.NO_MATCH;
812     }
813     if (!config.assertsEnabled()
814         && enclosingBlockPath.getLeaf().getKind().equals(Tree.Kind.ASSERT)) {
815       return Description.NO_MATCH;
816     }
817     if (!relevantInitializerMethodOrBlock(enclosingBlockPath, state)) {
818       return Description.NO_MATCH;
819     }
820 
821     // now, make sure we have a field read
822     Symbol symbol = ASTHelpers.getSymbol(tree);
823     if (symbol == null) {
824       return Description.NO_MATCH;
825     }
826     if (!symbol.getKind().equals(ElementKind.FIELD)) {
827       return Description.NO_MATCH;
828     }
829     // for static fields, make sure the enclosing init is a static method or block
830     if (symbol.isStatic()) {
831       Tree enclosing = enclosingBlockPath.getLeaf();
832       if (enclosing instanceof MethodTree
833           && !ASTHelpers.getSymbol((MethodTree) enclosing).isStatic()) {
834         return Description.NO_MATCH;
835       } else if (enclosing instanceof BlockTree && !((BlockTree) enclosing).isStatic()) {
836         return Description.NO_MATCH;
837       }
838     }
839     if (okToReadBeforeInitialized(path)) {
840       // writing the field, not reading it
841       return Description.NO_MATCH;
842     }
843 
844     // check that the field might actually be problematic to read
845     FieldInitEntities entities = class2Entities.get(enclosingClassSymbol(enclosingBlockPath));
846     if (!(entities.nonnullInstanceFields().contains(symbol)
847         || entities.nonnullStaticFields().contains(symbol))) {
848       // field is either nullable or initialized at declaration
849       return Description.NO_MATCH;
850     }
851     if (errorBuilder.symbolHasSuppressWarningsAnnotation(symbol, INITIALIZATION_CHECK_NAME)) {
852       // also suppress checking read before init, as we may not find explicit initialization
853       return Description.NO_MATCH;
854     }
855     return checkPossibleUninitFieldRead(tree, state, symbol, path, enclosingBlockPath);
856   }
857 
enclosingClassSymbol(TreePath enclosingBlockPath)858   private Symbol.ClassSymbol enclosingClassSymbol(TreePath enclosingBlockPath) {
859     Tree leaf = enclosingBlockPath.getLeaf();
860     if (leaf instanceof BlockTree) {
861       // parent must be a ClassTree
862       Tree parent = enclosingBlockPath.getParentPath().getLeaf();
863       return ASTHelpers.getSymbol((ClassTree) parent);
864     } else {
865       return ASTHelpers.enclosingClass(ASTHelpers.getSymbol(leaf));
866     }
867   }
868 
relevantInitializerMethodOrBlock( TreePath enclosingBlockPath, VisitorState state)869   private boolean relevantInitializerMethodOrBlock(
870       TreePath enclosingBlockPath, VisitorState state) {
871     Tree methodLambdaOrBlock = enclosingBlockPath.getLeaf();
872     if (methodLambdaOrBlock instanceof LambdaExpressionTree) {
873       return false;
874     } else if (methodLambdaOrBlock instanceof MethodTree) {
875       MethodTree methodTree = (MethodTree) methodLambdaOrBlock;
876       if (isConstructor(methodTree) && !constructorInvokesAnother(methodTree, state)) {
877         return true;
878       }
879       if (ASTHelpers.getSymbol(methodTree).isStatic()) {
880         Set<MethodTree> staticInitializerMethods =
881             class2Entities.get(enclosingClassSymbol(enclosingBlockPath)).staticInitializerMethods();
882         return staticInitializerMethods.size() == 1
883             && staticInitializerMethods.contains(methodTree);
884       } else {
885         Set<MethodTree> instanceInitializerMethods =
886             class2Entities
887                 .get(enclosingClassSymbol(enclosingBlockPath))
888                 .instanceInitializerMethods();
889         return instanceInitializerMethods.size() == 1
890             && instanceInitializerMethods.contains(methodTree);
891       }
892     } else {
893       // initializer or field declaration
894       return true;
895     }
896   }
897 
checkPossibleUninitFieldRead( ExpressionTree tree, VisitorState state, Symbol symbol, TreePath path, TreePath enclosingBlockPath)898   private Description checkPossibleUninitFieldRead(
899       ExpressionTree tree,
900       VisitorState state,
901       Symbol symbol,
902       TreePath path,
903       TreePath enclosingBlockPath) {
904     if (!fieldInitializedByPreviousInitializer(symbol, enclosingBlockPath, state)
905         && !fieldAlwaysInitializedBeforeRead(symbol, path, state, enclosingBlockPath)) {
906       ErrorMessage errorMessage =
907           new ErrorMessage(
908               MessageTypes.NONNULL_FIELD_READ_BEFORE_INIT,
909               "read of @NonNull field " + symbol + " before initialization");
910       return errorBuilder.createErrorDescription(errorMessage, buildDescription(tree), state);
911     } else {
912       return Description.NO_MATCH;
913     }
914   }
915 
916   /**
917    * @param symbol the field being read
918    * @param pathToRead TreePath to the read operation
919    * @param state visitor state
920    * @param enclosingBlockPath TreePath to enclosing initializer block
921    * @return true if within the initializer, the field is always initialized before the read
922    *     operation, false otherwise
923    */
fieldAlwaysInitializedBeforeRead( Symbol symbol, TreePath pathToRead, VisitorState state, TreePath enclosingBlockPath)924   private boolean fieldAlwaysInitializedBeforeRead(
925       Symbol symbol, TreePath pathToRead, VisitorState state, TreePath enclosingBlockPath) {
926     AccessPathNullnessAnalysis nullnessAnalysis = getNullnessAnalysis(state);
927     Set<Element> nonnullFields;
928     if (symbol.isStatic()) {
929       nonnullFields = nullnessAnalysis.getNonnullStaticFieldsBefore(pathToRead, state.context);
930     } else {
931       nonnullFields = new LinkedHashSet<>();
932       nonnullFields.addAll(
933           nullnessAnalysis.getNonnullFieldsOfReceiverBefore(pathToRead, state.context));
934       nonnullFields.addAll(safeInitByCalleeBefore(pathToRead, state, enclosingBlockPath));
935     }
936     return nonnullFields.contains(symbol);
937   }
938 
939   /**
940    * computes those fields always initialized by callee safe init methods before a read operation
941    * (pathToRead) is invoked. See <a
942    * href="https://github.com/uber/NullAway/wiki/Error-Messages#initializer-method-does-not-guarantee-nonnull-field-is-initialized--nonnull-field--not-initialized">the
943    * docs</a> for what is considered a safe initializer method.
944    */
safeInitByCalleeBefore( TreePath pathToRead, VisitorState state, TreePath enclosingBlockPath)945   private ImmutableSet<Element> safeInitByCalleeBefore(
946       TreePath pathToRead, VisitorState state, TreePath enclosingBlockPath) {
947     Set<Element> safeInitMethods = new LinkedHashSet<>();
948     Tree enclosingBlockOrMethod = enclosingBlockPath.getLeaf();
949     if (enclosingBlockOrMethod instanceof VariableTree) {
950       return ImmutableSet.of();
951     }
952     ImmutableSet.Builder<Element> resultBuilder = ImmutableSet.builder();
953     BlockTree blockTree =
954         enclosingBlockOrMethod instanceof BlockTree
955             ? (BlockTree) enclosingBlockOrMethod
956             : ((MethodTree) enclosingBlockOrMethod).getBody();
957     List<? extends StatementTree> statements = blockTree.getStatements();
958     Tree readExprTree = pathToRead.getLeaf();
959     int readStartPos = getStartPos((JCTree) readExprTree);
960     TreePath classTreePath = enclosingBlockPath;
961     // look for the parent ClassTree node, which represents the enclosing class / enum / interface
962     while (!(classTreePath.getLeaf() instanceof ClassTree)) {
963       classTreePath = classTreePath.getParentPath();
964       if (classTreePath == null) {
965         throw new IllegalStateException(
966             "could not find enclosing class / enum / interface for "
967                 + state.getSourceForNode(enclosingBlockPath.getLeaf()));
968       }
969     }
970     Symbol.ClassSymbol classSymbol = ASTHelpers.getSymbol((ClassTree) classTreePath.getLeaf());
971     for (int i = 0; i < statements.size(); i++) {
972       StatementTree curStmt = statements.get(i);
973       if (getStartPos((JCTree) curStmt) <= readStartPos) {
974         Element privMethodElem = getInvokeOfSafeInitMethod(curStmt, classSymbol, state);
975         if (privMethodElem != null) {
976           safeInitMethods.add(privMethodElem);
977         }
978         // Hack: Handling try{...}finally{...} statement, see getSafeInitMethods
979         if (curStmt.getKind().equals(Tree.Kind.TRY)) {
980           TryTree tryTree = (TryTree) curStmt;
981           // ToDo: Should we check initialization inside tryTree.getResources ? What is the scope of
982           // that initialization?
983           if (tryTree.getCatches().size() == 0) {
984             if (tryTree.getBlock() != null) {
985               resultBuilder.addAll(
986                   safeInitByCalleeBefore(
987                       pathToRead, state, new TreePath(enclosingBlockPath, tryTree.getBlock())));
988             }
989             if (tryTree.getFinallyBlock() != null) {
990               resultBuilder.addAll(
991                   safeInitByCalleeBefore(
992                       pathToRead,
993                       state,
994                       new TreePath(enclosingBlockPath, tryTree.getFinallyBlock())));
995             }
996           }
997         }
998       }
999     }
1000     addGuaranteedNonNullFromInvokes(
1001         state, getTreesInstance(state), safeInitMethods, getNullnessAnalysis(state), resultBuilder);
1002     return resultBuilder.build();
1003   }
1004 
getStartPos(JCTree tree)1005   private int getStartPos(JCTree tree) {
1006     return tree.pos().getStartPosition();
1007   }
1008 
1009   /**
1010    * @param fieldSymbol the field
1011    * @param initTreePath TreePath to the initializer method / block
1012    * @param state visitor state
1013    * @return true if the field is always initialized (by some other initializer) before the
1014    *     initializer corresponding to initTreePath executes
1015    */
fieldInitializedByPreviousInitializer( Symbol fieldSymbol, TreePath initTreePath, VisitorState state)1016   private boolean fieldInitializedByPreviousInitializer(
1017       Symbol fieldSymbol, TreePath initTreePath, VisitorState state) {
1018     TreePath enclosingClassPath = initTreePath.getParentPath();
1019     ClassTree enclosingClass = (ClassTree) enclosingClassPath.getLeaf();
1020     Multimap<Tree, Element> tree2Init =
1021         initTree2PrevFieldInit.get(ASTHelpers.getSymbol(enclosingClass));
1022     if (tree2Init == null) {
1023       tree2Init = computeTree2Init(enclosingClassPath, state);
1024       initTree2PrevFieldInit.put(ASTHelpers.getSymbol(enclosingClass), tree2Init);
1025     }
1026     return tree2Init.containsEntry(initTreePath.getLeaf(), fieldSymbol);
1027   }
1028 
1029   /**
1030    * @param enclosingClassPath TreePath to class
1031    * @param state visitor state
1032    * @return a map from each initializer <em>i</em> to the fields known to be initialized before
1033    *     <em>i</em> executes
1034    */
computeTree2Init( TreePath enclosingClassPath, VisitorState state)1035   private Multimap<Tree, Element> computeTree2Init(
1036       TreePath enclosingClassPath, VisitorState state) {
1037     ClassTree enclosingClass = (ClassTree) enclosingClassPath.getLeaf();
1038     ImmutableMultimap.Builder<Tree, Element> builder = ImmutableMultimap.builder();
1039     // NOTE: this set includes both instance and static fields
1040     Set<Element> initThusFar = new LinkedHashSet<>();
1041     Set<MethodTree> constructors = new LinkedHashSet<>();
1042     AccessPathNullnessAnalysis nullnessAnalysis = getNullnessAnalysis(state);
1043     // NOTE: we assume the members are returned in their syntactic order.  This has held
1044     // true in our testing
1045     for (Tree memberTree : enclosingClass.getMembers()) {
1046       if (memberTree instanceof VariableTree || memberTree instanceof BlockTree) {
1047         // putAll does not keep a reference to initThusFar, so we don't need to make a copy here
1048         builder.putAll(memberTree, initThusFar);
1049       }
1050       if (memberTree instanceof BlockTree) {
1051         BlockTree blockTree = (BlockTree) memberTree;
1052         // add whatever gets initialized here
1053         TreePath memberPath = new TreePath(enclosingClassPath, memberTree);
1054         if (blockTree.isStatic()) {
1055           initThusFar.addAll(
1056               nullnessAnalysis.getNonnullStaticFieldsAtExit(memberPath, state.context));
1057         } else {
1058           initThusFar.addAll(
1059               nullnessAnalysis.getNonnullFieldsOfReceiverAtExit(memberPath, state.context));
1060         }
1061       }
1062       if (memberTree instanceof MethodTree) {
1063         MethodTree methodTree = (MethodTree) memberTree;
1064         if (isConstructor(methodTree)) {
1065           constructors.add(methodTree);
1066         }
1067       }
1068     }
1069     // all the initializer blocks have run before any code inside a constructor
1070     constructors.stream().forEach((c) -> builder.putAll(c, initThusFar));
1071     Symbol.ClassSymbol classSymbol = ASTHelpers.getSymbol(enclosingClass);
1072     FieldInitEntities entities = class2Entities.get(classSymbol);
1073     if (entities.instanceInitializerMethods().size() == 1) {
1074       MethodTree initMethod = entities.instanceInitializerMethods().iterator().next();
1075       // collect the fields that may not be initialized by *some* constructor NC
1076       Set<Symbol> constructorUninitSymbols = class2ConstructorUninit.get(classSymbol);
1077       // fields initialized after constructors is initThusFar + (nonNullFields - constructorUninit)
1078       Sets.SetView<Element> initAfterConstructors =
1079           Sets.union(
1080               initThusFar,
1081               Sets.difference(entities.nonnullInstanceFields(), constructorUninitSymbols));
1082       builder.putAll(initMethod, initAfterConstructors);
1083     }
1084     if (entities.staticInitializerMethods().size() == 1) {
1085       MethodTree staticInitMethod = entities.staticInitializerMethods().iterator().next();
1086       // constructors aren't relevant here; just use initThusFar
1087       builder.putAll(staticInitMethod, initThusFar);
1088     }
1089     return builder.build();
1090   }
1091 
1092   /**
1093    * @param path tree path to read operation
1094    * @return true if it is permissible to perform this read before the field has been initialized,
1095    *     false otherwise
1096    */
okToReadBeforeInitialized(TreePath path)1097   private boolean okToReadBeforeInitialized(TreePath path) {
1098     TreePath parentPath = path.getParentPath();
1099     Tree leaf = path.getLeaf();
1100     Tree parent = parentPath.getLeaf();
1101     if (parent instanceof AssignmentTree) {
1102       // ok if it's actually a write
1103       AssignmentTree assignment = (AssignmentTree) parent;
1104       return assignment.getVariable().equals(leaf);
1105     } else if (parent instanceof BinaryTree) {
1106       // ok if we're comparing to null
1107       BinaryTree binaryTree = (BinaryTree) parent;
1108       Tree.Kind kind = binaryTree.getKind();
1109       if (kind.equals(Tree.Kind.EQUAL_TO) || kind.equals(Tree.Kind.NOT_EQUAL_TO)) {
1110         ExpressionTree left = binaryTree.getLeftOperand();
1111         ExpressionTree right = binaryTree.getRightOperand();
1112         return (left.equals(leaf) && right.getKind().equals(Tree.Kind.NULL_LITERAL))
1113             || (right.equals(leaf) && left.getKind().equals(Tree.Kind.NULL_LITERAL));
1114       }
1115     } else if (parent instanceof MethodInvocationTree) {
1116       // ok if it's invoking castToNonNull and the read is the argument
1117       MethodInvocationTree methodInvoke = (MethodInvocationTree) parent;
1118       Symbol.MethodSymbol methodSymbol = ASTHelpers.getSymbol(methodInvoke);
1119       String qualifiedName =
1120           ASTHelpers.enclosingClass(methodSymbol) + "." + methodSymbol.getSimpleName().toString();
1121       if (qualifiedName.equals(config.getCastToNonNullMethod())) {
1122         List<? extends ExpressionTree> arguments = methodInvoke.getArguments();
1123         return arguments.size() == 1 && leaf.equals(arguments.get(0));
1124       }
1125     }
1126     return false;
1127   }
1128 
1129   @Override
matchVariable(VariableTree tree, VisitorState state)1130   public Description matchVariable(VariableTree tree, VisitorState state) {
1131     if (!matchWithinTopLevelClass) {
1132       return Description.NO_MATCH;
1133     }
1134     VarSymbol symbol = ASTHelpers.getSymbol(tree);
1135     if (symbol.type.isPrimitive() && tree.getInitializer() != null) {
1136       return doUnboxingCheck(state, tree.getInitializer());
1137     }
1138     if (!symbol.getKind().equals(ElementKind.FIELD)) {
1139       return Description.NO_MATCH;
1140     }
1141     ExpressionTree initializer = tree.getInitializer();
1142     if (initializer != null) {
1143       if (!symbol.type.isPrimitive() && !skipDueToFieldAnnotation(symbol)) {
1144         if (mayBeNullExpr(state, initializer)) {
1145           final ErrorMessage errorMessage =
1146               new ErrorMessage(
1147                   MessageTypes.ASSIGN_FIELD_NULLABLE,
1148                   "assigning @Nullable expression to @NonNull field");
1149           return errorBuilder.createErrorDescriptionForNullAssignment(
1150               errorMessage, initializer, buildDescription(tree), state);
1151         }
1152       }
1153     }
1154     return Description.NO_MATCH;
1155   }
1156 
1157   @Override
matchClass(ClassTree tree, VisitorState state)1158   public Description matchClass(ClassTree tree, VisitorState state) {
1159     // Check if the class is excluded according to the filter
1160     // if so, set the flag to match within the class to false
1161     // NOTE: for this mechanism to work, we rely on the enclosing ClassTree
1162     // always being visited before code within that class.  We also
1163     // assume that a single checker object is not being
1164     // used from multiple threads
1165     // We don't want to update the flag for nested classes.
1166     // Ideally we would keep a stack of flags to handle nested types,
1167     // but this is not easy within the Error Prone APIs.
1168     // Instead, we use this flag as an optimization, skipping work if the
1169     // top-level class is to be skipped. If a nested class should be
1170     // skipped, we instead rely on last-minute suppression of the
1171     // error message, using the mechanism in
1172     // ErrorBuilder.hasPathSuppression(...)
1173     Symbol.ClassSymbol classSymbol = ASTHelpers.getSymbol(tree);
1174     NestingKind nestingKind = classSymbol.getNestingKind();
1175     if (!nestingKind.isNested()) {
1176       matchWithinTopLevelClass = !isExcludedClass(classSymbol);
1177       // since we are processing a new top-level class, invalidate any cached
1178       // results for previous classes
1179       handler.onMatchTopLevelClass(this, tree, state, classSymbol);
1180       getNullnessAnalysis(state).invalidateCaches();
1181       initTree2PrevFieldInit.clear();
1182       class2Entities.clear();
1183       class2ConstructorUninit.clear();
1184       computedNullnessMap.clear();
1185       EnclosingEnvironmentNullness.instance(state.context).clear();
1186     }
1187     if (matchWithinTopLevelClass) {
1188       // we need to update the environment before checking field initialization, as the latter
1189       // may run dataflow analysis
1190       if (nestingKind.equals(NestingKind.LOCAL) || nestingKind.equals(NestingKind.ANONYMOUS)) {
1191         updateEnvironmentMapping(tree, state);
1192       }
1193       checkFieldInitialization(tree, state);
1194     }
1195     return Description.NO_MATCH;
1196   }
1197 
1198   // UNBOXING CHECKS
1199 
1200   @Override
matchBinary(BinaryTree tree, VisitorState state)1201   public Description matchBinary(BinaryTree tree, VisitorState state) {
1202     if (!matchWithinTopLevelClass) {
1203       return Description.NO_MATCH;
1204     }
1205     ExpressionTree leftOperand = tree.getLeftOperand();
1206     ExpressionTree rightOperand = tree.getRightOperand();
1207     Type leftType = ASTHelpers.getType(leftOperand);
1208     Type rightType = ASTHelpers.getType(rightOperand);
1209     if (leftType == null || rightType == null) {
1210       throw new RuntimeException();
1211     }
1212     if (leftType.isPrimitive() && !rightType.isPrimitive()) {
1213       return doUnboxingCheck(state, rightOperand);
1214     }
1215     if (rightType.isPrimitive() && !leftType.isPrimitive()) {
1216       return doUnboxingCheck(state, leftOperand);
1217     }
1218     return Description.NO_MATCH;
1219   }
1220 
1221   @Override
matchUnary(UnaryTree tree, VisitorState state)1222   public Description matchUnary(UnaryTree tree, VisitorState state) {
1223     if (!matchWithinTopLevelClass) {
1224       return Description.NO_MATCH;
1225     }
1226     return doUnboxingCheck(state, tree.getExpression());
1227   }
1228 
1229   @Override
matchConditionalExpression( ConditionalExpressionTree tree, VisitorState state)1230   public Description matchConditionalExpression(
1231       ConditionalExpressionTree tree, VisitorState state) {
1232     if (!matchWithinTopLevelClass) {
1233       return Description.NO_MATCH;
1234     }
1235     return doUnboxingCheck(state, tree.getCondition());
1236   }
1237 
1238   @Override
matchIf(IfTree tree, VisitorState state)1239   public Description matchIf(IfTree tree, VisitorState state) {
1240     if (!matchWithinTopLevelClass) {
1241       return Description.NO_MATCH;
1242     }
1243     return doUnboxingCheck(state, tree.getCondition());
1244   }
1245 
1246   @Override
matchWhileLoop(WhileLoopTree tree, VisitorState state)1247   public Description matchWhileLoop(WhileLoopTree tree, VisitorState state) {
1248     if (!matchWithinTopLevelClass) {
1249       return Description.NO_MATCH;
1250     }
1251     return doUnboxingCheck(state, tree.getCondition());
1252   }
1253 
1254   @Override
matchForLoop(ForLoopTree tree, VisitorState state)1255   public Description matchForLoop(ForLoopTree tree, VisitorState state) {
1256     if (!matchWithinTopLevelClass) {
1257       return Description.NO_MATCH;
1258     }
1259     if (tree.getCondition() != null) {
1260       return doUnboxingCheck(state, tree.getCondition());
1261     }
1262     return Description.NO_MATCH;
1263   }
1264 
1265   @Override
matchEnhancedForLoop(EnhancedForLoopTree tree, VisitorState state)1266   public Description matchEnhancedForLoop(EnhancedForLoopTree tree, VisitorState state) {
1267     if (!matchWithinTopLevelClass) {
1268       return Description.NO_MATCH;
1269     }
1270     ExpressionTree expr = tree.getExpression();
1271     final ErrorMessage errorMessage =
1272         new ErrorMessage(
1273             MessageTypes.DEREFERENCE_NULLABLE,
1274             "enhanced-for expression " + state.getSourceForNode(expr) + " is @Nullable");
1275     if (mayBeNullExpr(state, expr)) {
1276       return errorBuilder.createErrorDescription(errorMessage, buildDescription(expr), state);
1277     }
1278     return Description.NO_MATCH;
1279   }
1280 
1281   /**
1282    * if any expression has non-primitive type, we should check that it can't be null as it is
1283    * getting unboxed
1284    *
1285    * @param expressions expressions to check
1286    * @return error Description if an error is found, otherwise NO_MATCH
1287    */
doUnboxingCheck(VisitorState state, ExpressionTree... expressions)1288   private Description doUnboxingCheck(VisitorState state, ExpressionTree... expressions) {
1289     for (ExpressionTree tree : expressions) {
1290       Type type = ASTHelpers.getType(tree);
1291       if (type == null) {
1292         throw new RuntimeException("was not expecting null type");
1293       }
1294       if (!type.isPrimitive()) {
1295         if (mayBeNullExpr(state, tree)) {
1296           final ErrorMessage errorMessage =
1297               new ErrorMessage(MessageTypes.UNBOX_NULLABLE, "unboxing of a @Nullable value");
1298           return errorBuilder.createErrorDescription(errorMessage, buildDescription(tree), state);
1299         }
1300       }
1301     }
1302     return Description.NO_MATCH;
1303   }
1304 
1305   /**
1306    * handle either a method invocation or a 'new' invocation
1307    *
1308    * @param tree the corresponding MethodInvocationTree or NewClassTree
1309    * @param state visitor state
1310    * @param methodSymbol symbol for invoked method
1311    * @param actualParams parameters passed at call
1312    * @return description of error or NO_MATCH if no error
1313    */
handleInvocation( Tree tree, VisitorState state, Symbol.MethodSymbol methodSymbol, List<? extends ExpressionTree> actualParams)1314   private Description handleInvocation(
1315       Tree tree,
1316       VisitorState state,
1317       Symbol.MethodSymbol methodSymbol,
1318       List<? extends ExpressionTree> actualParams) {
1319     ImmutableSet<Integer> nonNullPositions = null;
1320     if (NullabilityUtil.isUnannotated(methodSymbol, config)) {
1321       nonNullPositions =
1322           handler.onUnannotatedInvocationGetNonNullPositions(
1323               this, state, methodSymbol, actualParams, ImmutableSet.of());
1324     }
1325     List<VarSymbol> formalParams = methodSymbol.getParameters();
1326 
1327     if (formalParams.size() != actualParams.size()
1328         && !methodSymbol.isVarArgs()
1329         && !methodSymbol.isStatic()
1330         && methodSymbol.isConstructor()
1331         && methodSymbol.enclClass().isInner()) {
1332       // In special cases like one in issue #366
1333       // formal params and actual params do not match while using JDK11+
1334       // we bail out in this particular case
1335       return Description.NO_MATCH;
1336     }
1337 
1338     if (nonNullPositions == null) {
1339       ImmutableSet.Builder<Integer> builder = ImmutableSet.builder();
1340       // compute which arguments are @NonNull
1341       for (int i = 0; i < formalParams.size(); i++) {
1342         VarSymbol param = formalParams.get(i);
1343         if (param.type.isPrimitive()) {
1344           Description unboxingCheck = doUnboxingCheck(state, actualParams.get(i));
1345           if (unboxingCheck != Description.NO_MATCH) {
1346             return unboxingCheck;
1347           } else {
1348             continue;
1349           }
1350         }
1351         // we need to call paramHasNullableAnnotation here since the invoked method may be defined
1352         // in a class file
1353         if (!Nullness.paramHasNullableAnnotation(methodSymbol, i, config)) {
1354           builder.add(i);
1355         }
1356       }
1357       nonNullPositions = builder.build();
1358     }
1359 
1360     // now actually check the arguments
1361     // NOTE: the case of an invocation on a possibly-null reference
1362     // is handled by matchMemberSelect()
1363     for (int argPos : nonNullPositions) {
1364       ExpressionTree actual = null;
1365       boolean mayActualBeNull = false;
1366       if (argPos == formalParams.size() - 1 && methodSymbol.isVarArgs()) {
1367         // Check all vararg actual arguments for nullability
1368         if (actualParams.size() <= argPos) {
1369           continue;
1370         }
1371         for (ExpressionTree arg : actualParams.subList(argPos, actualParams.size())) {
1372           actual = arg;
1373           mayActualBeNull = mayBeNullExpr(state, actual);
1374           if (mayActualBeNull) {
1375             break;
1376           }
1377         }
1378       } else {
1379         actual = actualParams.get(argPos);
1380         mayActualBeNull = mayBeNullExpr(state, actual);
1381       }
1382       // This statement should be unreachable without assigning actual beforehand:
1383       Preconditions.checkNotNull(actual);
1384       // make sure we are passing a non-null value
1385       if (mayActualBeNull) {
1386         String message =
1387             "passing @Nullable parameter '"
1388                 + state.getSourceForNode(actual)
1389                 + "' where @NonNull is required";
1390         state.reportMatch(
1391             errorBuilder.createErrorDescriptionForNullAssignment(
1392                 new ErrorMessage(MessageTypes.PASS_NULLABLE, message),
1393                 actual,
1394                 buildDescription(actual),
1395                 state));
1396       }
1397     }
1398     // Check for @NonNull being passed to castToNonNull (if configured)
1399     return checkCastToNonNullTakesNullable(tree, state, methodSymbol, actualParams);
1400   }
1401 
checkCastToNonNullTakesNullable( Tree tree, VisitorState state, Symbol.MethodSymbol methodSymbol, List<? extends ExpressionTree> actualParams)1402   private Description checkCastToNonNullTakesNullable(
1403       Tree tree,
1404       VisitorState state,
1405       Symbol.MethodSymbol methodSymbol,
1406       List<? extends ExpressionTree> actualParams) {
1407     String qualifiedName =
1408         ASTHelpers.enclosingClass(methodSymbol) + "." + methodSymbol.getSimpleName().toString();
1409     if (qualifiedName.equals(config.getCastToNonNullMethod())) {
1410       if (actualParams.size() != 1) {
1411         throw new RuntimeException(
1412             "Invalid number of parameters passed to configured CastToNonNullMethod.");
1413       }
1414       ExpressionTree actual = actualParams.get(0);
1415       TreePath enclosingMethodOrLambda =
1416           NullabilityUtil.findEnclosingMethodOrLambdaOrInitializer(state.getPath());
1417       boolean isInitializer;
1418       if (enclosingMethodOrLambda == null) {
1419         throw new RuntimeException("no enclosing method, lambda or initializer!");
1420       } else if (enclosingMethodOrLambda.getLeaf() instanceof LambdaExpressionTree) {
1421         isInitializer = false;
1422       } else if (enclosingMethodOrLambda.getLeaf() instanceof MethodTree) {
1423         MethodTree enclosingMethod = (MethodTree) enclosingMethodOrLambda.getLeaf();
1424         isInitializer = isInitializerMethod(state, ASTHelpers.getSymbol(enclosingMethod));
1425       } else {
1426         // Initializer block
1427         isInitializer = true;
1428       }
1429       if (!isInitializer && !mayBeNullExpr(state, actual)) {
1430         String message =
1431             "passing known @NonNull parameter '"
1432                 + state.getSourceForNode(actual)
1433                 + "' to CastToNonNullMethod ("
1434                 + qualifiedName
1435                 + "). This method should only take arguments that NullAway considers @Nullable "
1436                 + "at the invocation site, but which are known not to be null at runtime.";
1437         return errorBuilder.createErrorDescription(
1438             new ErrorMessage(MessageTypes.CAST_TO_NONNULL_ARG_NONNULL, message),
1439             tree,
1440             buildDescription(tree),
1441             state);
1442       }
1443     }
1444     return Description.NO_MATCH;
1445   }
1446 
1447   /**
1448    * check that all @NonNull fields of the class are properly initialized
1449    *
1450    * @param tree the class
1451    * @param state visitor state
1452    */
checkFieldInitialization(ClassTree tree, VisitorState state)1453   private void checkFieldInitialization(ClassTree tree, VisitorState state) {
1454     FieldInitEntities entities = collectEntities(tree, state);
1455     Symbol.ClassSymbol classSymbol = ASTHelpers.getSymbol(tree);
1456     class2Entities.put(classSymbol, entities);
1457     // set of all non-null instance fields f such that *some* constructor does not initialize f
1458     Set<Symbol> notInitializedInConstructors;
1459     SetMultimap<MethodTree, Symbol> constructorInitInfo;
1460     if (entities.constructors().isEmpty()) {
1461       constructorInitInfo = null;
1462       notInitializedInConstructors = entities.nonnullInstanceFields();
1463     } else {
1464       constructorInitInfo = checkConstructorInitialization(entities, state);
1465       notInitializedInConstructors = ImmutableSet.copyOf(constructorInitInfo.values());
1466     }
1467     // Filter out final fields, since javac will already check initialization
1468     notInitializedInConstructors =
1469         ImmutableSet.copyOf(
1470             Sets.filter(
1471                 notInitializedInConstructors,
1472                 symbol -> !symbol.getModifiers().contains(Modifier.FINAL)));
1473     class2ConstructorUninit.putAll(classSymbol, notInitializedInConstructors);
1474     Set<Symbol> notInitializedAtAll =
1475         notAssignedInAnyInitializer(entities, notInitializedInConstructors, state);
1476     SetMultimap<Element, Element> errorFieldsForInitializer = LinkedHashMultimap.create();
1477     // non-null if we have a single initializer method
1478     Symbol.MethodSymbol singleInitializerMethod = null;
1479     if (entities.instanceInitializerMethods().size() == 1) {
1480       singleInitializerMethod =
1481           ASTHelpers.getSymbol(entities.instanceInitializerMethods().iterator().next());
1482     }
1483     for (Symbol uninitField : notInitializedAtAll) {
1484       if (errorBuilder.symbolHasSuppressWarningsAnnotation(
1485           uninitField, INITIALIZATION_CHECK_NAME)) {
1486         continue;
1487       }
1488       if (singleInitializerMethod != null) {
1489         // report it on the initializer
1490         errorFieldsForInitializer.put(singleInitializerMethod, uninitField);
1491       } else if (constructorInitInfo == null) {
1492         // report it on the field, except in the case where the class is externalInit and
1493         // we have no initializer methods
1494         if (!(isExternalInit(classSymbol) && entities.instanceInitializerMethods().isEmpty())) {
1495           errorBuilder.reportInitErrorOnField(
1496               uninitField, state, buildDescription(getTreesInstance(state).getTree(uninitField)));
1497         }
1498       } else {
1499         // report it on each constructor that does not initialize it
1500         for (MethodTree methodTree : constructorInitInfo.keySet()) {
1501           Set<Symbol> uninitFieldsForConstructor = constructorInitInfo.get(methodTree);
1502           if (uninitFieldsForConstructor.contains(uninitField)) {
1503             errorFieldsForInitializer.put(ASTHelpers.getSymbol(methodTree), uninitField);
1504           }
1505         }
1506       }
1507     }
1508     for (Element constructorElement : errorFieldsForInitializer.keySet()) {
1509       errorBuilder.reportInitializerError(
1510           (Symbol.MethodSymbol) constructorElement,
1511           errMsgForInitializer(errorFieldsForInitializer.get(constructorElement), state),
1512           state,
1513           buildDescription(getTreesInstance(state).getTree(constructorElement)));
1514     }
1515     // For static fields
1516     Set<Symbol> notInitializedStaticFields = notInitializedStatic(entities, state);
1517     for (Symbol uninitSField : notInitializedStaticFields) {
1518       // Always report it on the field for static fields (can't do @SuppressWarnings on a static
1519       // initialization block
1520       // anyways).
1521       errorBuilder.reportInitErrorOnField(
1522           uninitSField, state, buildDescription(getTreesInstance(state).getTree(uninitSField)));
1523     }
1524   }
1525 
1526   /**
1527    * @param entities relevant entities from class
1528    * @param notInitializedInConstructors those fields not initialized in some constructor
1529    * @param state visitor state
1530    * @return those fields from notInitializedInConstructors that are not initialized in any
1531    *     initializer method
1532    */
notAssignedInAnyInitializer( FieldInitEntities entities, Set<Symbol> notInitializedInConstructors, VisitorState state)1533   private Set<Symbol> notAssignedInAnyInitializer(
1534       FieldInitEntities entities, Set<Symbol> notInitializedInConstructors, VisitorState state) {
1535     Trees trees = getTreesInstance(state);
1536     Symbol.ClassSymbol classSymbol = entities.classSymbol();
1537     ImmutableSet.Builder<Element> initInSomeInitializerBuilder = ImmutableSet.builder();
1538     for (MethodTree initMethodTree : entities.instanceInitializerMethods()) {
1539       if (initMethodTree.getBody() == null) {
1540         continue;
1541       }
1542       addInitializedFieldsForBlock(
1543           state,
1544           trees,
1545           classSymbol,
1546           initInSomeInitializerBuilder,
1547           initMethodTree.getBody(),
1548           new TreePath(state.getPath(), initMethodTree));
1549     }
1550     for (BlockTree block : entities.instanceInitializerBlocks()) {
1551       addInitializedFieldsForBlock(
1552           state,
1553           trees,
1554           classSymbol,
1555           initInSomeInitializerBuilder,
1556           block,
1557           new TreePath(state.getPath(), block));
1558     }
1559     Set<Symbol> result = new LinkedHashSet<>();
1560     ImmutableSet<Element> initInSomeInitializer = initInSomeInitializerBuilder.build();
1561     for (Symbol fieldSymbol : notInitializedInConstructors) {
1562       if (!initInSomeInitializer.contains(fieldSymbol)) {
1563         result.add(fieldSymbol);
1564       }
1565     }
1566     return result;
1567   }
1568 
addInitializedFieldsForBlock( VisitorState state, Trees trees, Symbol.ClassSymbol classSymbol, ImmutableSet.Builder<Element> initInSomeInitializerBuilder, BlockTree block, TreePath path)1569   private void addInitializedFieldsForBlock(
1570       VisitorState state,
1571       Trees trees,
1572       Symbol.ClassSymbol classSymbol,
1573       ImmutableSet.Builder<Element> initInSomeInitializerBuilder,
1574       BlockTree block,
1575       TreePath path) {
1576     AccessPathNullnessAnalysis nullnessAnalysis = getNullnessAnalysis(state);
1577     Set<Element> nonnullAtExit =
1578         nullnessAnalysis.getNonnullFieldsOfReceiverAtExit(path, state.context);
1579     initInSomeInitializerBuilder.addAll(nonnullAtExit);
1580     Set<Element> safeInitMethods = getSafeInitMethods(block, classSymbol, state);
1581     addGuaranteedNonNullFromInvokes(
1582         state, trees, safeInitMethods, nullnessAnalysis, initInSomeInitializerBuilder);
1583   }
1584 
1585   /**
1586    * @param entities field init info
1587    * @param state visitor state
1588    * @return a map from each constructor C to the nonnull fields that C does *not* initialize
1589    */
checkConstructorInitialization( FieldInitEntities entities, VisitorState state)1590   private SetMultimap<MethodTree, Symbol> checkConstructorInitialization(
1591       FieldInitEntities entities, VisitorState state) {
1592     SetMultimap<MethodTree, Symbol> result = LinkedHashMultimap.create();
1593     Set<Symbol> nonnullInstanceFields = entities.nonnullInstanceFields();
1594     Trees trees = getTreesInstance(state);
1595     boolean isExternalInit = isExternalInit(entities.classSymbol());
1596     for (MethodTree constructor : entities.constructors()) {
1597       if (constructorInvokesAnother(constructor, state)) {
1598         continue;
1599       }
1600       if (constructor.getParameters().size() == 0 && isExternalInit) {
1601         // external framework initializes fields in this case
1602         continue;
1603       }
1604       Set<Element> guaranteedNonNull =
1605           guaranteedNonNullForConstructor(entities, state, trees, constructor);
1606       for (Symbol fieldSymbol : nonnullInstanceFields) {
1607         if (!guaranteedNonNull.contains(fieldSymbol)) {
1608           result.put(constructor, fieldSymbol);
1609         }
1610       }
1611     }
1612     return result;
1613   }
1614 
isExternalInit(Symbol.ClassSymbol classSymbol)1615   private boolean isExternalInit(Symbol.ClassSymbol classSymbol) {
1616     return StreamSupport.stream(NullabilityUtil.getAllAnnotations(classSymbol).spliterator(), false)
1617         .map((anno) -> anno.getAnnotationType().toString())
1618         .anyMatch(config::isExternalInitClassAnnotation);
1619   }
1620 
guaranteedNonNullForConstructor( FieldInitEntities entities, VisitorState state, Trees trees, MethodTree constructor)1621   private ImmutableSet<Element> guaranteedNonNullForConstructor(
1622       FieldInitEntities entities, VisitorState state, Trees trees, MethodTree constructor) {
1623     Set<Element> safeInitMethods =
1624         getSafeInitMethods(constructor.getBody(), entities.classSymbol(), state);
1625     AccessPathNullnessAnalysis nullnessAnalysis = getNullnessAnalysis(state);
1626     ImmutableSet.Builder<Element> guaranteedNonNullBuilder = ImmutableSet.builder();
1627     guaranteedNonNullBuilder.addAll(
1628         nullnessAnalysis.getNonnullFieldsOfReceiverAtExit(
1629             new TreePath(state.getPath(), constructor), state.context));
1630     addGuaranteedNonNullFromInvokes(
1631         state, trees, safeInitMethods, nullnessAnalysis, guaranteedNonNullBuilder);
1632     return guaranteedNonNullBuilder.build();
1633   }
1634 
1635   /** does the constructor invoke another constructor in the same class via this(...)? */
constructorInvokesAnother(MethodTree constructor, VisitorState state)1636   private boolean constructorInvokesAnother(MethodTree constructor, VisitorState state) {
1637     BlockTree body = constructor.getBody();
1638     List<? extends StatementTree> statements = body.getStatements();
1639     if (statements.size() > 0) {
1640       StatementTree statementTree = statements.get(0);
1641       if (isThisCall(statementTree, state)) {
1642         return true;
1643       }
1644     }
1645     return false;
1646   }
1647 
notInitializedStatic(FieldInitEntities entities, VisitorState state)1648   private Set<Symbol> notInitializedStatic(FieldInitEntities entities, VisitorState state) {
1649     Set<Symbol> nonNullStaticFields = entities.nonnullStaticFields();
1650     Set<Element> initializedInStaticInitializers = new LinkedHashSet<Element>();
1651     AccessPathNullnessAnalysis nullnessAnalysis = getNullnessAnalysis(state);
1652     for (BlockTree initializer : entities.staticInitializerBlocks()) {
1653       Set<Element> nonnullAtExit =
1654           nullnessAnalysis.getNonnullStaticFieldsAtExit(
1655               new TreePath(state.getPath(), initializer), state.context);
1656       initializedInStaticInitializers.addAll(nonnullAtExit);
1657     }
1658     for (MethodTree initializerMethod : entities.staticInitializerMethods()) {
1659       Set<Element> nonnullAtExit =
1660           nullnessAnalysis.getNonnullStaticFieldsAtExit(
1661               new TreePath(state.getPath(), initializerMethod), state.context);
1662       initializedInStaticInitializers.addAll(nonnullAtExit);
1663     }
1664     Set<Symbol> notInitializedStaticFields = new LinkedHashSet<Symbol>();
1665     for (Symbol field : nonNullStaticFields) {
1666       if (!initializedInStaticInitializers.contains(field)) {
1667         notInitializedStaticFields.add(field);
1668       }
1669     }
1670     return notInitializedStaticFields;
1671   }
1672 
addGuaranteedNonNullFromInvokes( VisitorState state, Trees trees, Set<Element> safeInitMethods, AccessPathNullnessAnalysis nullnessAnalysis, ImmutableSet.Builder<Element> guaranteedNonNullBuilder)1673   private void addGuaranteedNonNullFromInvokes(
1674       VisitorState state,
1675       Trees trees,
1676       Set<Element> safeInitMethods,
1677       AccessPathNullnessAnalysis nullnessAnalysis,
1678       ImmutableSet.Builder<Element> guaranteedNonNullBuilder) {
1679     for (Element invoked : safeInitMethods) {
1680       Tree invokedTree = trees.getTree(invoked);
1681       guaranteedNonNullBuilder.addAll(
1682           nullnessAnalysis.getNonnullFieldsOfReceiverAtExit(
1683               new TreePath(state.getPath(), invokedTree), state.context));
1684     }
1685   }
1686 
1687   /**
1688    * @param blockTree block of statements
1689    * @param state visitor state
1690    * @return Elements of safe init methods that are invoked as top-level statements in the method
1691    */
getSafeInitMethods( BlockTree blockTree, Symbol.ClassSymbol classSymbol, VisitorState state)1692   private Set<Element> getSafeInitMethods(
1693       BlockTree blockTree, Symbol.ClassSymbol classSymbol, VisitorState state) {
1694     Set<Element> result = new LinkedHashSet<>();
1695     List<? extends StatementTree> statements = blockTree.getStatements();
1696     for (StatementTree stmt : statements) {
1697       Element privMethodElem = getInvokeOfSafeInitMethod(stmt, classSymbol, state);
1698       if (privMethodElem != null) {
1699         result.add(privMethodElem);
1700       }
1701       // Hack: If we see a try{...}finally{...} statement, without a catch, we consider the methods
1702       // inside both blocks
1703       // as "top level" for the purposes of finding initialization methods. Any exception happening
1704       // there is also an
1705       // exception of the full method.
1706       if (stmt.getKind().equals(Tree.Kind.TRY)) {
1707         TryTree tryTree = (TryTree) stmt;
1708         if (tryTree.getCatches().size() == 0) {
1709           if (tryTree.getBlock() != null) {
1710             result.addAll(getSafeInitMethods(tryTree.getBlock(), classSymbol, state));
1711           }
1712           if (tryTree.getFinallyBlock() != null) {
1713             result.addAll(getSafeInitMethods(tryTree.getFinallyBlock(), classSymbol, state));
1714           }
1715         }
1716       }
1717     }
1718     return result;
1719   }
1720 
1721   /**
1722    * A safe init method is an instance method that is either private or final (so no overriding is
1723    * possible)
1724    *
1725    * @param stmt the statement
1726    * @param enclosingClassSymbol symbol for enclosing constructor / initializer
1727    * @param state visitor state
1728    * @return element of safe init function if stmt invokes that function; null otherwise
1729    */
1730   @Nullable
getInvokeOfSafeInitMethod( StatementTree stmt, final Symbol.ClassSymbol enclosingClassSymbol, VisitorState state)1731   private Element getInvokeOfSafeInitMethod(
1732       StatementTree stmt, final Symbol.ClassSymbol enclosingClassSymbol, VisitorState state) {
1733     Matcher<ExpressionTree> invokeMatcher =
1734         (expressionTree, s) -> {
1735           if (!(expressionTree instanceof MethodInvocationTree)) {
1736             return false;
1737           }
1738           MethodInvocationTree methodInvocationTree = (MethodInvocationTree) expressionTree;
1739           Symbol.MethodSymbol symbol = ASTHelpers.getSymbol(methodInvocationTree);
1740           Set<Modifier> modifiers = symbol.getModifiers();
1741           Set<Modifier> classModifiers = enclosingClassSymbol.getModifiers();
1742           if ((symbol.isPrivate()
1743                   || modifiers.contains(Modifier.FINAL)
1744                   || classModifiers.contains(Modifier.FINAL))
1745               && !symbol.isStatic()
1746               && !modifiers.contains(Modifier.NATIVE)) {
1747             // check it's the same class (could be an issue with inner classes)
1748             if (ASTHelpers.enclosingClass(symbol).equals(enclosingClassSymbol)) {
1749               // make sure the receiver is 'this'
1750               ExpressionTree receiver = ASTHelpers.getReceiver(expressionTree);
1751               return receiver == null || isThisIdentifier(receiver);
1752             }
1753           }
1754           return false;
1755         };
1756     if (stmt.getKind().equals(EXPRESSION_STATEMENT)) {
1757       ExpressionTree expression = ((ExpressionStatementTree) stmt).getExpression();
1758       if (invokeMatcher.matches(expression, state)) {
1759         return ASTHelpers.getSymbol(expression);
1760       }
1761     }
1762     return null;
1763   }
1764 
isThisCall(StatementTree statementTree, VisitorState state)1765   private boolean isThisCall(StatementTree statementTree, VisitorState state) {
1766     if (statementTree.getKind().equals(EXPRESSION_STATEMENT)) {
1767       ExpressionTree expression = ((ExpressionStatementTree) statementTree).getExpression();
1768       return Matchers.methodInvocation(THIS_MATCHER).matches(expression, state);
1769     }
1770     return false;
1771   }
1772 
collectEntities(ClassTree tree, VisitorState state)1773   private FieldInitEntities collectEntities(ClassTree tree, VisitorState state) {
1774     Symbol.ClassSymbol classSymbol = ASTHelpers.getSymbol(tree);
1775     Set<Symbol> nonnullInstanceFields = new LinkedHashSet<>();
1776     Set<Symbol> nonnullStaticFields = new LinkedHashSet<>();
1777     List<BlockTree> instanceInitializerBlocks = new ArrayList<>();
1778     List<BlockTree> staticInitializerBlocks = new ArrayList<>();
1779     Set<MethodTree> constructors = new LinkedHashSet<>();
1780     Set<MethodTree> instanceInitializerMethods = new LinkedHashSet<>();
1781     Set<MethodTree> staticInitializerMethods = new LinkedHashSet<>();
1782 
1783     // we assume getMembers() returns members in the same order as the declarations
1784     for (Tree memberTree : tree.getMembers()) {
1785       if (TreeUtils.isClassTree(memberTree)) {
1786         // do nothing
1787         continue;
1788       }
1789       switch (memberTree.getKind()) {
1790         case METHOD:
1791           // check if it is a constructor or an @Initializer method
1792           MethodTree methodTree = (MethodTree) memberTree;
1793           Symbol.MethodSymbol symbol = ASTHelpers.getSymbol(methodTree);
1794           if (isConstructor(methodTree)) {
1795             constructors.add(methodTree);
1796           } else if (isInitializerMethod(state, symbol)) {
1797             if (symbol.isStatic()) {
1798               staticInitializerMethods.add(methodTree);
1799             } else {
1800               instanceInitializerMethods.add(methodTree);
1801             }
1802           }
1803           break;
1804         case VARIABLE:
1805           // field declaration
1806           VariableTree varTree = (VariableTree) memberTree;
1807           Symbol fieldSymbol = ASTHelpers.getSymbol(varTree);
1808           if (fieldSymbol.type.isPrimitive() || skipDueToFieldAnnotation(fieldSymbol)) {
1809             continue;
1810           }
1811           if (varTree.getInitializer() != null) {
1812             // note that we check that the initializer does the right thing in
1813             // matchVariable()
1814             continue;
1815           }
1816           if (fieldSymbol.isStatic()) {
1817             nonnullStaticFields.add(fieldSymbol);
1818           } else {
1819             nonnullInstanceFields.add(fieldSymbol);
1820           }
1821           break;
1822         case BLOCK:
1823           // initializer block
1824           BlockTree blockTree = (BlockTree) memberTree;
1825           if (blockTree.isStatic()) {
1826             staticInitializerBlocks.add(blockTree);
1827           } else {
1828             instanceInitializerBlocks.add(blockTree);
1829           }
1830           break;
1831         default:
1832           throw new RuntimeException(
1833               memberTree.getKind().toString() + " " + state.getSourceForNode(memberTree));
1834       }
1835     }
1836 
1837     return FieldInitEntities.create(
1838         classSymbol,
1839         ImmutableSet.copyOf(nonnullInstanceFields),
1840         ImmutableSet.copyOf(nonnullStaticFields),
1841         ImmutableList.copyOf(instanceInitializerBlocks),
1842         ImmutableList.copyOf(staticInitializerBlocks),
1843         ImmutableSet.copyOf(constructors),
1844         ImmutableSet.copyOf(instanceInitializerMethods),
1845         ImmutableSet.copyOf(staticInitializerMethods));
1846   }
1847 
isConstructor(MethodTree methodTree)1848   private boolean isConstructor(MethodTree methodTree) {
1849     return ASTHelpers.getSymbol(methodTree).isConstructor()
1850         && !ASTHelpers.isGeneratedConstructor(methodTree);
1851   }
1852 
isInitializerMethod(VisitorState state, Symbol.MethodSymbol symbol)1853   private boolean isInitializerMethod(VisitorState state, Symbol.MethodSymbol symbol) {
1854     if (ASTHelpers.hasDirectAnnotationWithSimpleName(symbol, "Initializer")
1855         || config.isKnownInitializerMethod(symbol)) {
1856       return true;
1857     }
1858     for (AnnotationMirror anno : symbol.getAnnotationMirrors()) {
1859       String annoTypeStr = anno.getAnnotationType().toString();
1860       if (config.isInitializerMethodAnnotation(annoTypeStr)) {
1861         return true;
1862       }
1863     }
1864     Symbol.MethodSymbol closestOverriddenMethod =
1865         NullabilityUtil.getClosestOverriddenMethod(symbol, state.getTypes());
1866     if (closestOverriddenMethod == null) {
1867       return false;
1868     }
1869     return isInitializerMethod(state, closestOverriddenMethod);
1870   }
1871 
skipDueToFieldAnnotation(Symbol fieldSymbol)1872   private boolean skipDueToFieldAnnotation(Symbol fieldSymbol) {
1873     return NullabilityUtil.getAllAnnotations(fieldSymbol)
1874         .map(anno -> anno.getAnnotationType().toString())
1875         .anyMatch(config::isExcludedFieldAnnotation);
1876   }
1877 
isExcludedClass(Symbol.ClassSymbol classSymbol)1878   private boolean isExcludedClass(Symbol.ClassSymbol classSymbol) {
1879     String className = classSymbol.getQualifiedName().toString();
1880     if (config.isExcludedClass(className)) {
1881       return true;
1882     }
1883     if (!config.fromAnnotatedPackage(classSymbol)) {
1884       return true;
1885     }
1886     // check annotations
1887     ImmutableSet<String> excludedClassAnnotations = config.getExcludedClassAnnotations();
1888     return classSymbol
1889         .getAnnotationMirrors()
1890         .stream()
1891         .map(anno -> anno.getAnnotationType().toString())
1892         .anyMatch(excludedClassAnnotations::contains);
1893   }
1894 
mayBeNullExpr(VisitorState state, ExpressionTree expr)1895   private boolean mayBeNullExpr(VisitorState state, ExpressionTree expr) {
1896     expr = stripParensAndCasts(expr);
1897     if (ASTHelpers.constValue(expr) != null) {
1898       // This should include literals such as "true" or a string
1899       // obviously not null
1900       return false;
1901     }
1902     // the logic here is to avoid doing dataflow analysis whenever possible
1903     Symbol exprSymbol = ASTHelpers.getSymbol(expr);
1904     boolean exprMayBeNull;
1905     switch (expr.getKind()) {
1906       case NULL_LITERAL:
1907         // obviously null
1908         exprMayBeNull = true;
1909         break;
1910       case ARRAY_ACCESS:
1911         // unsound!  we cannot check for nullness of array contents yet
1912         exprMayBeNull = false;
1913         break;
1914       case NEW_CLASS:
1915       case NEW_ARRAY:
1916         // for string concatenation, auto-boxing
1917       case LAMBDA_EXPRESSION:
1918         // Lambdas may return null, but the lambda literal itself should not be null
1919       case MEMBER_REFERENCE:
1920         // These cannot be null; the compiler would catch it
1921       case MULTIPLY_ASSIGNMENT:
1922       case DIVIDE_ASSIGNMENT:
1923       case REMAINDER_ASSIGNMENT:
1924       case PLUS_ASSIGNMENT:
1925       case MINUS_ASSIGNMENT:
1926       case LEFT_SHIFT_ASSIGNMENT:
1927       case RIGHT_SHIFT_ASSIGNMENT:
1928       case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
1929       case AND_ASSIGNMENT:
1930       case XOR_ASSIGNMENT:
1931       case OR_ASSIGNMENT:
1932         // result of compound assignment cannot be null
1933       case PLUS:
1934         // rest are for auto-boxing
1935       case MINUS:
1936       case MULTIPLY:
1937       case DIVIDE:
1938       case REMAINDER:
1939       case CONDITIONAL_AND:
1940       case CONDITIONAL_OR:
1941       case LOGICAL_COMPLEMENT:
1942       case INSTANCE_OF:
1943       case PREFIX_INCREMENT:
1944       case PREFIX_DECREMENT:
1945       case POSTFIX_DECREMENT:
1946       case POSTFIX_INCREMENT:
1947       case EQUAL_TO:
1948       case NOT_EQUAL_TO:
1949       case GREATER_THAN:
1950       case GREATER_THAN_EQUAL:
1951       case LESS_THAN:
1952       case LESS_THAN_EQUAL:
1953       case UNARY_MINUS:
1954       case UNARY_PLUS:
1955       case AND:
1956       case OR:
1957       case XOR:
1958       case LEFT_SHIFT:
1959       case RIGHT_SHIFT:
1960       case UNSIGNED_RIGHT_SHIFT:
1961         // clearly not null
1962         exprMayBeNull = false;
1963         break;
1964       case MEMBER_SELECT:
1965         if (exprSymbol == null) {
1966           throw new IllegalStateException(
1967               "unexpected null symbol for dereference expression " + state.getSourceForNode(expr));
1968         }
1969         exprMayBeNull = mayBeNullFieldAccess(state, expr, exprSymbol);
1970         break;
1971       case IDENTIFIER:
1972         if (exprSymbol == null) {
1973           throw new IllegalStateException(
1974               "unexpected null symbol for identifier " + state.getSourceForNode(expr));
1975         }
1976         if (exprSymbol.getKind().equals(ElementKind.FIELD)) {
1977           // Special case: mayBeNullFieldAccess runs handler.onOverrideMayBeNullExpr before
1978           // dataflow.
1979           return mayBeNullFieldAccess(state, expr, exprSymbol);
1980         } else {
1981           // Check handler.onOverrideMayBeNullExpr before dataflow.
1982           exprMayBeNull = handler.onOverrideMayBeNullExpr(this, expr, state, true);
1983           return exprMayBeNull ? nullnessFromDataflow(state, expr) : false;
1984         }
1985       case METHOD_INVOCATION:
1986         // Special case: mayBeNullMethodCall runs handler.onOverrideMayBeNullExpr before dataflow.
1987         return mayBeNullMethodCall(state, expr, (Symbol.MethodSymbol) exprSymbol);
1988       case CONDITIONAL_EXPRESSION:
1989       case ASSIGNMENT:
1990         exprMayBeNull = nullnessFromDataflow(state, expr);
1991         break;
1992       default:
1993         // match switch expressions by comparing strings, so the code compiles on JDK versions < 12
1994         if (expr.getKind().name().equals("SWITCH_EXPRESSION")) {
1995           exprMayBeNull = nullnessFromDataflow(state, expr);
1996         } else {
1997           throw new RuntimeException(
1998               "whoops, better handle " + expr.getKind() + " " + state.getSourceForNode(expr));
1999         }
2000     }
2001     exprMayBeNull = handler.onOverrideMayBeNullExpr(this, expr, state, exprMayBeNull);
2002     return exprMayBeNull;
2003   }
2004 
mayBeNullMethodCall( VisitorState state, ExpressionTree expr, Symbol.MethodSymbol exprSymbol)2005   private boolean mayBeNullMethodCall(
2006       VisitorState state, ExpressionTree expr, Symbol.MethodSymbol exprSymbol) {
2007     boolean exprMayBeNull = true;
2008     if (NullabilityUtil.isUnannotated(exprSymbol, config)) {
2009       exprMayBeNull = false;
2010     }
2011     if (!Nullness.hasNullableAnnotation(exprSymbol, config)) {
2012       exprMayBeNull = false;
2013     }
2014     exprMayBeNull = handler.onOverrideMayBeNullExpr(this, expr, state, exprMayBeNull);
2015     return exprMayBeNull ? nullnessFromDataflow(state, expr) : false;
2016   }
2017 
nullnessFromDataflow(VisitorState state, ExpressionTree expr)2018   public boolean nullnessFromDataflow(VisitorState state, ExpressionTree expr) {
2019     Nullness nullness =
2020         getNullnessAnalysis(state).getNullness(new TreePath(state.getPath(), expr), state.context);
2021     if (nullness == null) {
2022       // this may be unsound, like for field initializers
2023       // figure out if we care
2024       return false;
2025     }
2026     return NullabilityUtil.nullnessToBool(nullness);
2027   }
2028 
getNullnessAnalysis(VisitorState state)2029   public AccessPathNullnessAnalysis getNullnessAnalysis(VisitorState state) {
2030     return AccessPathNullnessAnalysis.instance(state, nonAnnotatedMethod, config, this.handler);
2031   }
2032 
mayBeNullFieldAccess(VisitorState state, ExpressionTree expr, Symbol exprSymbol)2033   private boolean mayBeNullFieldAccess(VisitorState state, ExpressionTree expr, Symbol exprSymbol) {
2034     boolean exprMayBeNull = true;
2035     if (!NullabilityUtil.mayBeNullFieldFromType(exprSymbol, config)) {
2036       exprMayBeNull = false;
2037     }
2038     exprMayBeNull = handler.onOverrideMayBeNullExpr(this, expr, state, exprMayBeNull);
2039     return exprMayBeNull ? nullnessFromDataflow(state, expr) : false;
2040   }
2041 
matchDereference( ExpressionTree baseExpression, ExpressionTree derefExpression, VisitorState state)2042   private Description matchDereference(
2043       ExpressionTree baseExpression, ExpressionTree derefExpression, VisitorState state) {
2044     Symbol baseExpressionSymbol = ASTHelpers.getSymbol(baseExpression);
2045     // Note that a null dereference is possible even if baseExpressionSymbol is null,
2046     // e.g., in cases where baseExpression contains conditional logic (like a ternary
2047     // expression, or a switch expression in JDK 12+)
2048     if (baseExpressionSymbol != null) {
2049       if (baseExpressionSymbol.type.isPrimitive()
2050           || baseExpressionSymbol.getKind() == ElementKind.PACKAGE
2051           || ElementUtils.isTypeElement(baseExpressionSymbol)) {
2052         // we know we don't have a null dereference here
2053         return Description.NO_MATCH;
2054       }
2055     }
2056     if (mayBeNullExpr(state, baseExpression)) {
2057       final String message =
2058           "dereferenced expression " + state.getSourceForNode(baseExpression) + " is @Nullable";
2059       ErrorMessage errorMessage = new ErrorMessage(MessageTypes.DEREFERENCE_NULLABLE, message);
2060 
2061       return errorBuilder.createErrorDescriptionForNullAssignment(
2062           errorMessage, baseExpression, buildDescription(derefExpression), state);
2063     }
2064 
2065     Optional<ErrorMessage> handlerErrorMessage =
2066         handler.onExpressionDereference(derefExpression, baseExpression, state);
2067     if (handlerErrorMessage.isPresent()) {
2068       return errorBuilder.createErrorDescriptionForNullAssignment(
2069           handlerErrorMessage.get(), derefExpression, buildDescription(derefExpression), state);
2070     }
2071 
2072     return Description.NO_MATCH;
2073   }
2074 
2075   @SuppressWarnings("unused")
changeReturnNullabilityFix( Tree suggestTree, Description.Builder builder, VisitorState state)2076   private Description.Builder changeReturnNullabilityFix(
2077       Tree suggestTree, Description.Builder builder, VisitorState state) {
2078     if (suggestTree.getKind() != Tree.Kind.METHOD) {
2079       throw new RuntimeException("This should be a MethodTree");
2080     }
2081     SuggestedFix.Builder fixBuilder = SuggestedFix.builder();
2082     MethodTree methodTree = (MethodTree) suggestTree;
2083     int countNullableAnnotations = 0;
2084     for (AnnotationTree annotationTree : methodTree.getModifiers().getAnnotations()) {
2085       if (state.getSourceForNode(annotationTree.getAnnotationType()).endsWith("Nullable")) {
2086         fixBuilder.delete(annotationTree);
2087         countNullableAnnotations += 1;
2088       }
2089     }
2090     assert countNullableAnnotations > 1;
2091     return builder.addFix(fixBuilder.build());
2092   }
2093 
2094   @SuppressWarnings("unused")
changeParamNullabilityFix( Tree suggestTree, Description.Builder builder)2095   private Description.Builder changeParamNullabilityFix(
2096       Tree suggestTree, Description.Builder builder) {
2097     return builder.addFix(SuggestedFix.prefixWith(suggestTree, "@Nullable "));
2098   }
2099 
2100   @SuppressWarnings("unused")
depth(ExpressionTree expression)2101   private int depth(ExpressionTree expression) {
2102     switch (expression.getKind()) {
2103       case MEMBER_SELECT:
2104         MemberSelectTree selectTree = (MemberSelectTree) expression;
2105         return 1 + depth(selectTree.getExpression());
2106       case METHOD_INVOCATION:
2107         MethodInvocationTree invTree = (MethodInvocationTree) expression;
2108         return depth(invTree.getMethodSelect());
2109       case IDENTIFIER:
2110         IdentifierTree varTree = (IdentifierTree) expression;
2111         Symbol symbol = ASTHelpers.getSymbol(varTree);
2112         return symbol.getKind().equals(ElementKind.FIELD) ? 2 : 1;
2113       default:
2114         return 0;
2115     }
2116   }
2117 
isThisIdentifier(ExpressionTree expressionTree)2118   private static boolean isThisIdentifier(ExpressionTree expressionTree) {
2119     return expressionTree.getKind().equals(IDENTIFIER)
2120         && ((IdentifierTree) expressionTree).getName().toString().equals("this");
2121   }
2122 
isThisIdentifierMatcher( ExpressionTree expressionTree, VisitorState state)2123   private static boolean isThisIdentifierMatcher(
2124       ExpressionTree expressionTree, VisitorState state) {
2125     return isThisIdentifier(expressionTree);
2126   }
2127 
getErrorBuilder()2128   public ErrorBuilder getErrorBuilder() {
2129     return errorBuilder;
2130   }
2131 
2132   /**
2133    * strip out enclosing parentheses, type casts and Nullchk operators.
2134    *
2135    * @param expr a potentially parenthesised expression.
2136    * @return the same expression without parentheses.
2137    */
stripParensAndCasts(ExpressionTree expr)2138   private static ExpressionTree stripParensAndCasts(ExpressionTree expr) {
2139     boolean someChange = true;
2140     while (someChange) {
2141       someChange = false;
2142       if (expr.getKind().equals(PARENTHESIZED)) {
2143         expr = ((ParenthesizedTree) expr).getExpression();
2144         someChange = true;
2145       }
2146       if (expr.getKind().equals(TYPE_CAST)) {
2147         expr = ((TypeCastTree) expr).getExpression();
2148         someChange = true;
2149       }
2150 
2151       // Strips Nullchk operator
2152       if (expr.getKind().equals(OTHER) && expr instanceof JCTree.JCUnary) {
2153         expr = ((JCTree.JCUnary) expr).getExpression();
2154         someChange = true;
2155       }
2156     }
2157     return expr;
2158   }
2159 
2160   /**
2161    * Returns the computed nullness information from an expression. If none is available, it returns
2162    * Nullable.
2163    *
2164    * <p>Computed information can be added by handlers or by the core, and should supersede that
2165    * comming from annotations.
2166    *
2167    * <p>The default value of an expression without additional computed nullness information is
2168    * always Nullable, since this method should only be called when the fact that the expression is
2169    * NonNull is not clear from looking at annotations.
2170    *
2171    * @param e an expression
2172    * @return computed nullness for e, if any, else Nullable
2173    */
getComputedNullness(ExpressionTree e)2174   public Nullness getComputedNullness(ExpressionTree e) {
2175     if (computedNullnessMap.containsKey(e)) {
2176       return computedNullnessMap.get(e);
2177     } else {
2178       return Nullness.NULLABLE;
2179     }
2180   }
2181 
2182   /**
2183    * Add computed nullness information to an expression.
2184    *
2185    * <p>Used by handlers to communicate that an expression should has a more precise nullness than
2186    * what is known from source annotations.
2187    *
2188    * @param e any expression in the AST.
2189    * @param nullness the added nullness information.
2190    */
setComputedNullness(ExpressionTree e, Nullness nullness)2191   public void setComputedNullness(ExpressionTree e, Nullness nullness) {
2192     computedNullnessMap.put(e, nullness);
2193   }
2194 
2195   @AutoValue
2196   abstract static class FieldInitEntities {
2197 
create( Symbol.ClassSymbol classSymbol, Set<Symbol> nonnullInstanceFields, Set<Symbol> nonnullStaticFields, List<BlockTree> instanceInitializerBlocks, List<BlockTree> staticInitializerBlocks, Set<MethodTree> constructors, Set<MethodTree> instanceInitializerMethods, Set<MethodTree> staticInitializerMethods)2198     static FieldInitEntities create(
2199         Symbol.ClassSymbol classSymbol,
2200         Set<Symbol> nonnullInstanceFields,
2201         Set<Symbol> nonnullStaticFields,
2202         List<BlockTree> instanceInitializerBlocks,
2203         List<BlockTree> staticInitializerBlocks,
2204         Set<MethodTree> constructors,
2205         Set<MethodTree> instanceInitializerMethods,
2206         Set<MethodTree> staticInitializerMethods) {
2207       return new AutoValue_NullAway_FieldInitEntities(
2208           classSymbol,
2209           ImmutableSet.copyOf(nonnullInstanceFields),
2210           ImmutableSet.copyOf(nonnullStaticFields),
2211           ImmutableList.copyOf(instanceInitializerBlocks),
2212           ImmutableList.copyOf(staticInitializerBlocks),
2213           ImmutableSet.copyOf(constructors),
2214           ImmutableSet.copyOf(instanceInitializerMethods),
2215           ImmutableSet.copyOf(staticInitializerMethods));
2216     }
2217 
2218     /** @return symbol for class */
classSymbol()2219     abstract Symbol.ClassSymbol classSymbol();
2220 
2221     /**
2222      * @return <code>@NonNull</code> instance fields that are not directly initialized at
2223      *     declaration
2224      */
nonnullInstanceFields()2225     abstract ImmutableSet<Symbol> nonnullInstanceFields();
2226 
2227     /**
2228      * @return <code>@NonNull</code> static fields that are not directly initialized at declaration
2229      */
nonnullStaticFields()2230     abstract ImmutableSet<Symbol> nonnullStaticFields();
2231 
2232     /**
2233      * @return the list of instance initializer blocks (e.g. blocks of the form `class X { { //Code
2234      *     } } ), in the order in which they appear in the class
2235      */
instanceInitializerBlocks()2236     abstract ImmutableList<BlockTree> instanceInitializerBlocks();
2237 
2238     /**
2239      * @return the list of static initializer blocks (e.g. blocks of the form `class X { static {
2240      *     //Code } } ), in the order in which they appear in the class
2241      */
staticInitializerBlocks()2242     abstract ImmutableList<BlockTree> staticInitializerBlocks();
2243 
2244     /** @return the list of constructor */
constructors()2245     abstract ImmutableSet<MethodTree> constructors();
2246 
2247     /**
2248      * @return the list of non-static (instance) initializer methods. This includes methods
2249      *     annotated @Initializer, as well as those specified by -XepOpt:NullAway:KnownInitializers
2250      *     or annotated with annotations passed to -XepOpt:NullAway:CustomInitializerAnnotations
2251      */
instanceInitializerMethods()2252     abstract ImmutableSet<MethodTree> instanceInitializerMethods();
2253 
2254     /**
2255      * @return the list of static initializer methods. This includes static methods
2256      *     annotated @Initializer, as well as those specified by -XepOpt:NullAway:KnownInitializers
2257      *     or annotated with annotations passed to -XepOpt:NullAway:CustomInitializerAnnotations
2258      */
staticInitializerMethods()2259     abstract ImmutableSet<MethodTree> staticInitializerMethods();
2260   }
2261 }
2262