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