• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.uber.nullaway.handlers;
2 
3 import com.google.common.base.Preconditions;
4 import com.google.errorprone.util.ASTHelpers;
5 import com.sun.source.tree.MethodInvocationTree;
6 import com.sun.tools.javac.code.Symbol;
7 import com.uber.nullaway.dataflow.cfg.NullAwayCFGBuilder;
8 import javax.annotation.Nullable;
9 import javax.lang.model.element.Name;
10 import javax.lang.model.type.TypeMirror;
11 import org.checkerframework.nullaway.dataflow.cfg.node.MethodInvocationNode;
12 
13 /**
14  * Handler to expose semantics of Guava routines like {@code checkState}, {@code checkArgument}, and
15  * {@code verify} that check a boolean condition and fail with an exception if it is false.
16  */
17 public class GuavaAssertionsHandler extends BaseNoOpHandler {
18 
19   private static final String PRECONDITIONS_CLASS_NAME = "com.google.common.base.Preconditions";
20   private static final String CHECK_ARGUMENT_METHOD_NAME = "checkArgument";
21   private static final String CHECK_STATE_METHOD_NAME = "checkState";
22   private static final String VERIFY_CLASS_NAME = "com.google.common.base.Verify";
23   private static final String VERIFY_METHOD_NAME = "verify";
24 
25   @Nullable private Name preconditionsClass;
26   @Nullable private Name verifyClass;
27   @Nullable private Name checkArgumentMethod;
28   @Nullable private Name checkStateMethod;
29   @Nullable private Name verifyMethod;
30   @Nullable TypeMirror preconditionCheckArgumentErrorType;
31   @Nullable TypeMirror preconditionCheckStateErrorType;
32   @Nullable TypeMirror verifyErrorType;
33 
34   @Override
onCFGBuildPhase1AfterVisitMethodInvocation( NullAwayCFGBuilder.NullAwayCFGTranslationPhaseOne phase, MethodInvocationTree tree, MethodInvocationNode originalNode)35   public MethodInvocationNode onCFGBuildPhase1AfterVisitMethodInvocation(
36       NullAwayCFGBuilder.NullAwayCFGTranslationPhaseOne phase,
37       MethodInvocationTree tree,
38       MethodInvocationNode originalNode) {
39     Symbol.MethodSymbol callee = ASTHelpers.getSymbol(tree);
40     if (preconditionsClass == null) {
41       preconditionsClass = callee.name.table.fromString(PRECONDITIONS_CLASS_NAME);
42       verifyClass = callee.name.table.fromString(VERIFY_CLASS_NAME);
43       checkArgumentMethod = callee.name.table.fromString(CHECK_ARGUMENT_METHOD_NAME);
44       checkStateMethod = callee.name.table.fromString(CHECK_STATE_METHOD_NAME);
45       verifyMethod = callee.name.table.fromString(VERIFY_METHOD_NAME);
46       preconditionCheckArgumentErrorType = phase.classToErrorType(IllegalArgumentException.class);
47       preconditionCheckStateErrorType = phase.classToErrorType(IllegalStateException.class);
48       // We treat the Verify.* APIs as throwing a RuntimeException to avoid any issues with
49       // the VerifyException that they actually throw not being in the classpath (this will not
50       // affect the analysis result)
51       verifyErrorType = phase.classToErrorType(RuntimeException.class);
52     }
53     Preconditions.checkNotNull(preconditionCheckArgumentErrorType);
54     Preconditions.checkNotNull(preconditionCheckStateErrorType);
55     Preconditions.checkNotNull(verifyErrorType);
56     if (callee.enclClass().getQualifiedName().equals(preconditionsClass)
57         && !callee.getParameters().isEmpty()) {
58       // Attempt to match Precondition check methods to the expected exception type, providing as
59       // much context as possible for static analysis.
60       // In practice this may not be strictly necessary because the conditional throw is inserted
61       // after the method invocation, thus analysis must assume that the preconditions call is
62       // capable of throwing any unchecked throwable.
63       if (callee.name.equals(checkArgumentMethod)) {
64         phase.insertThrowOnFalse(originalNode.getArgument(0), preconditionCheckArgumentErrorType);
65       } else if (callee.name.equals(checkStateMethod)) {
66         phase.insertThrowOnFalse(originalNode.getArgument(0), preconditionCheckStateErrorType);
67       }
68     } else if (callee.enclClass().getQualifiedName().equals(verifyClass)
69         && !callee.getParameters().isEmpty()
70         && callee.name.equals(verifyMethod)) {
71       phase.insertThrowOnFalse(originalNode.getArgument(0), verifyErrorType);
72     }
73     return originalNode;
74   }
75 }
76