• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.uber.nullaway.handlers;
2 
3 /*
4  * Copyright (c) 2019 Uber Technologies, Inc.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
25 import com.google.errorprone.util.ASTHelpers;
26 import com.sun.tools.javac.code.Symbol;
27 import com.sun.tools.javac.util.Name;
28 import org.checkerframework.nullaway.dataflow.cfg.node.MethodInvocationNode;
29 import org.checkerframework.nullaway.dataflow.cfg.node.Node;
30 
31 /**
32  * A utility class that holds the names from the Table. Currently, {@link
33  * com.uber.nullaway.handlers.AssertionHandler} requires it, while {@link
34  * com.uber.nullaway.handlers.OptionalEmptinessHandler} uses it only when AssertionHandler is
35  * enabled.
36  */
37 class MethodNameUtil {
38 
39   // Strings corresponding to the names of the methods (and their owners) used to identify
40   // assertions in this handler.
41   private static final String IS_NOT_NULL_METHOD = "isNotNull";
42   private static final String IS_NOT_NULL_OWNER = "com.google.common.truth.Subject";
43   private static final String IS_TRUE_METHOD = "isTrue";
44   private static final String IS_TRUE_OWNER = "com.google.common.truth.BooleanSubject";
45   private static final String ASSERT_THAT_METHOD = "assertThat";
46   private static final String ASSERT_THAT_OWNER = "com.google.common.truth.Truth";
47 
48   private static final String HAMCREST_ASSERT_CLASS = "org.hamcrest.MatcherAssert";
49   private static final String JUNIT_ASSERT_CLASS = "org.junit.Assert";
50 
51   private static final String MATCHERS_CLASS = "org.hamcrest.Matchers";
52   private static final String CORE_MATCHERS_CLASS = "org.hamcrest.CoreMatchers";
53   private static final String CORE_IS_NULL_CLASS = "org.hamcrest.core.IsNull";
54   private static final String IS_MATCHER = "is";
55   private static final String NOT_MATCHER = "not";
56   private static final String NOT_NULL_VALUE_MATCHER = "notNullValue";
57   private static final String NULL_VALUE_MATCHER = "nullValue";
58 
59   // Names of the methods (and their owners) used to identify assertions in this handler. Name used
60   // here refers to com.sun.tools.javac.util.Name. Comparing methods using Names is faster than
61   // comparing using strings.
62   private Name isNotNull;
63   private Name isNotNullOwner;
64 
65   private Name isTrue;
66   private Name isTrueOwner;
67 
68   private Name assertThat;
69   private Name assertThatOwner;
70 
71   // Names for junit assertion libraries.
72   private Name hamcrestAssertClass;
73   private Name junitAssertClass;
74 
75   // Names for hamcrest matchers.
76   private Name matchersClass;
77   private Name coreMatchersClass;
78   private Name coreIsNullClass;
79   private Name isMatcher;
80   private Name notMatcher;
81   private Name notNullValueMatcher;
82   private Name nullValueMatcher;
83 
initializeMethodNames(Name.Table table)84   void initializeMethodNames(Name.Table table) {
85     isNotNull = table.fromString(IS_NOT_NULL_METHOD);
86     isNotNullOwner = table.fromString(IS_NOT_NULL_OWNER);
87 
88     isTrue = table.fromString(IS_TRUE_METHOD);
89     isTrueOwner = table.fromString(IS_TRUE_OWNER);
90 
91     assertThat = table.fromString(ASSERT_THAT_METHOD);
92     assertThatOwner = table.fromString(ASSERT_THAT_OWNER);
93 
94     hamcrestAssertClass = table.fromString(HAMCREST_ASSERT_CLASS);
95     junitAssertClass = table.fromString(JUNIT_ASSERT_CLASS);
96 
97     matchersClass = table.fromString(MATCHERS_CLASS);
98     coreMatchersClass = table.fromString(CORE_MATCHERS_CLASS);
99     coreIsNullClass = table.fromString(CORE_IS_NULL_CLASS);
100     isMatcher = table.fromString(IS_MATCHER);
101     notMatcher = table.fromString(NOT_MATCHER);
102     notNullValueMatcher = table.fromString(NOT_NULL_VALUE_MATCHER);
103     nullValueMatcher = table.fromString(NULL_VALUE_MATCHER);
104   }
105 
isMethodIsNotNull(Symbol.MethodSymbol methodSymbol)106   boolean isMethodIsNotNull(Symbol.MethodSymbol methodSymbol) {
107     return matchesMethod(methodSymbol, isNotNull, isNotNullOwner);
108   }
109 
isMethodIsTrue(Symbol.MethodSymbol methodSymbol)110   boolean isMethodIsTrue(Symbol.MethodSymbol methodSymbol) {
111     return matchesMethod(methodSymbol, isTrue, isTrueOwner);
112   }
113 
isMethodAssertThat(Symbol.MethodSymbol methodSymbol)114   boolean isMethodAssertThat(Symbol.MethodSymbol methodSymbol) {
115     return matchesMethod(methodSymbol, assertThat, assertThatOwner);
116   }
117 
isMethodHamcrestAssertThat(Symbol.MethodSymbol methodSymbol)118   boolean isMethodHamcrestAssertThat(Symbol.MethodSymbol methodSymbol) {
119     return matchesMethod(methodSymbol, assertThat, hamcrestAssertClass);
120   }
121 
isMethodJunitAssertThat(Symbol.MethodSymbol methodSymbol)122   boolean isMethodJunitAssertThat(Symbol.MethodSymbol methodSymbol) {
123     return matchesMethod(methodSymbol, assertThat, junitAssertClass);
124   }
125 
isMatcherIsNotNull(Node node)126   boolean isMatcherIsNotNull(Node node) {
127     // Matches with
128     //   * is(not(nullValue()))
129     //   * is(notNullValue())
130     if (matchesMatcherMethod(node, isMatcher, matchersClass)
131         || matchesMatcherMethod(node, isMatcher, coreMatchersClass)) {
132       // All overloads of `is` method have exactly one argument.
133       return isMatcherNotNull(((MethodInvocationNode) node).getArgument(0));
134     }
135     return false;
136   }
137 
isMatcherNotNull(Node node)138   private boolean isMatcherNotNull(Node node) {
139     // Matches with
140     //   * not(nullValue())
141     //   * notNullValue()
142     if (matchesMatcherMethod(node, notMatcher, matchersClass)
143         || matchesMatcherMethod(node, notMatcher, coreMatchersClass)) {
144       // All overloads of `not` method have exactly one argument.
145       return isMatcherNull(((MethodInvocationNode) node).getArgument(0));
146     }
147     return matchesMatcherMethod(node, notNullValueMatcher, matchersClass)
148         || matchesMatcherMethod(node, notNullValueMatcher, coreMatchersClass)
149         || matchesMatcherMethod(node, notNullValueMatcher, coreIsNullClass);
150   }
151 
isMatcherNull(Node node)152   private boolean isMatcherNull(Node node) {
153     // Matches with nullValue()
154     return matchesMatcherMethod(node, nullValueMatcher, matchersClass)
155         || matchesMatcherMethod(node, nullValueMatcher, coreMatchersClass)
156         || matchesMatcherMethod(node, nullValueMatcher, coreIsNullClass);
157   }
158 
matchesMatcherMethod(Node node, Name matcherName, Name matcherClass)159   private boolean matchesMatcherMethod(Node node, Name matcherName, Name matcherClass) {
160     if (node instanceof MethodInvocationNode) {
161       MethodInvocationNode methodInvocationNode = (MethodInvocationNode) node;
162       Symbol.MethodSymbol callee = ASTHelpers.getSymbol(methodInvocationNode.getTree());
163       return matchesMethod(callee, matcherName, matcherClass);
164     }
165     return false;
166   }
167 
matchesMethod( Symbol.MethodSymbol methodSymbol, Name toMatchMethodName, Name toMatchOwnerName)168   private boolean matchesMethod(
169       Symbol.MethodSymbol methodSymbol, Name toMatchMethodName, Name toMatchOwnerName) {
170     return methodSymbol.name.equals(toMatchMethodName)
171         && methodSymbol.owner.getQualifiedName().equals(toMatchOwnerName);
172   }
173 
isUtilInitialized()174   boolean isUtilInitialized() {
175     return isNotNull != null;
176   }
177 }
178