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