🌐 AI搜索 & 代理 主页
Skip to content

Commit 5209429

Browse files
authored
More flexible handling of AssertJ isNotNull methods (#1221)
Fixes #1219 Previously, we would only consider the `isNotNull` method of `org.assertj.core.api.AbstractAssert` to be valid. Now, we allow `isNotNull` of any class that implements the `org.assertj.core.api.Assert` interface, which handles the case from #1219 and more.
1 parent 6cfccfd commit 5209429

File tree

3 files changed

+55
-10
lines changed

3 files changed

+55
-10
lines changed

nullaway/src/main/java/com/uber/nullaway/handlers/AssertionHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public NullnessHint onDataflowVisitMethodInvocation(
6060
// Look for statements of the form: assertThat(A).isNotNull() or
6161
// assertThat(A).isInstanceOf(Foo.class)
6262
// A will not be NULL after this statement.
63-
if (methodNameUtil.isMethodIsNotNull(callee) || methodNameUtil.isMethodIsInstanceOf(callee)) {
63+
if (methodNameUtil.isMethodIsNotNull(callee, state)
64+
|| methodNameUtil.isMethodIsInstanceOf(callee, state)) {
6465
AccessPath ap = getAccessPathForNotNullAssertThatExpr(node, state, apContext);
6566
if (ap != null) {
6667
bothUpdates.set(ap, NONNULL);

nullaway/src/main/java/com/uber/nullaway/handlers/MethodNameUtil.java

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@
2222
* THE SOFTWARE.
2323
*/
2424

25+
import com.google.errorprone.VisitorState;
26+
import com.google.errorprone.suppliers.Supplier;
27+
import com.google.errorprone.suppliers.Suppliers;
2528
import com.google.errorprone.util.ASTHelpers;
2629
import com.sun.tools.javac.code.Symbol;
30+
import com.sun.tools.javac.code.Type;
2731
import com.sun.tools.javac.util.Name;
2832
import com.uber.nullaway.annotations.Initializer;
2933
import org.checkerframework.nullaway.dataflow.cfg.node.MethodInvocationNode;
@@ -41,8 +45,6 @@ class MethodNameUtil {
4145
// assertions in this handler.
4246
private static final String IS_NOT_NULL_METHOD = "isNotNull";
4347
private static final String IS_OWNER_TRUTH_SUBJECT = "com.google.common.truth.Subject";
44-
private static final String IS_OWNER_ASSERTJ_ABSTRACT_ASSERT =
45-
"org.assertj.core.api.AbstractAssert";
4648
private static final String IS_INSTANCE_OF_METHOD = "isInstanceOf";
4749
private static final String IS_INSTANCE_OF_ANY_METHOD = "isInstanceOfAny";
4850
private static final String IS_TRUE_METHOD = "isTrue";
@@ -79,6 +81,9 @@ class MethodNameUtil {
7981
private static final String NULL_VALUE_MATCHER = "nullValue";
8082
private static final String INSTANCE_OF_MATCHER = "instanceOf";
8183

84+
private static final Supplier<Type> ASSERTJ_ASSERT_TYPE_SUPPLIER =
85+
Suppliers.typeFromString("org.assertj.core.api.Assert");
86+
8287
// Names of the methods (and their owners) used to identify assertions in this handler. Name used
8388
// here refers to com.sun.tools.javac.util.Name. Comparing methods using Names is faster than
8489
// comparing using strings.
@@ -87,7 +92,6 @@ class MethodNameUtil {
8792
private Name isInstanceOf;
8893
private Name isInstanceOfAny;
8994
private Name isOwnerTruthSubject;
90-
private Name isOwnerAssertJAbstractAssert;
9195

9296
private Name isTrue;
9397
private Name isFalse;
@@ -130,7 +134,6 @@ class MethodNameUtil {
130134
void initializeMethodNames(Name.Table table) {
131135
isNotNull = table.fromString(IS_NOT_NULL_METHOD);
132136
isOwnerTruthSubject = table.fromString(IS_OWNER_TRUTH_SUBJECT);
133-
isOwnerAssertJAbstractAssert = table.fromString(IS_OWNER_ASSERTJ_ABSTRACT_ASSERT);
134137

135138
isInstanceOf = table.fromString(IS_INSTANCE_OF_METHOD);
136139
isInstanceOfAny = table.fromString(IS_INSTANCE_OF_ANY_METHOD);
@@ -172,16 +175,16 @@ void initializeMethodNames(Name.Table table) {
172175
instanceOfMatcher = table.fromString(INSTANCE_OF_MATCHER);
173176
}
174177

175-
boolean isMethodIsNotNull(Symbol.MethodSymbol methodSymbol) {
178+
boolean isMethodIsNotNull(Symbol.MethodSymbol methodSymbol, VisitorState state) {
176179
return matchesMethod(methodSymbol, isNotNull, isOwnerTruthSubject)
177-
|| matchesMethod(methodSymbol, isNotNull, isOwnerAssertJAbstractAssert);
180+
|| matchesAssertJAssertMethod(methodSymbol, isNotNull, state);
178181
}
179182

180-
boolean isMethodIsInstanceOf(Symbol.MethodSymbol methodSymbol) {
183+
boolean isMethodIsInstanceOf(Symbol.MethodSymbol methodSymbol, VisitorState state) {
181184
return matchesMethod(methodSymbol, isInstanceOf, isOwnerTruthSubject)
182-
|| matchesMethod(methodSymbol, isInstanceOf, isOwnerAssertJAbstractAssert)
185+
|| matchesAssertJAssertMethod(methodSymbol, isInstanceOf, state)
183186
// Truth doesn't seem to have isInstanceOfAny
184-
|| matchesMethod(methodSymbol, isInstanceOfAny, isOwnerAssertJAbstractAssert);
187+
|| matchesAssertJAssertMethod(methodSymbol, isInstanceOfAny, state);
185188
}
186189

187190
boolean isMethodAssertTrue(Symbol.MethodSymbol methodSymbol) {
@@ -303,6 +306,24 @@ private boolean matchesMethod(
303306
&& methodSymbol.owner.getQualifiedName().equals(toMatchOwnerName);
304307
}
305308

309+
/**
310+
* Checks if the method is an AssertJ assert method, i.e., it has the same name as
311+
* toMatchMethodName and its owner is a subtype of AssertJ's Assert class.
312+
*
313+
* @param methodSymbol the method symbol to check
314+
* @param toMatchMethodName the method name to match
315+
* @param state the visitor state
316+
* @return {@code true} if the method matches, {@code false} otherwise
317+
*/
318+
private boolean matchesAssertJAssertMethod(
319+
Symbol.MethodSymbol methodSymbol, Name toMatchMethodName, VisitorState state) {
320+
if (!methodSymbol.name.equals(toMatchMethodName)) {
321+
return false;
322+
}
323+
return ASTHelpers.isSubtype(
324+
methodSymbol.owner.type, ASSERTJ_ASSERT_TYPE_SUPPLIER.get(state), state);
325+
}
326+
306327
boolean isUtilInitialized() {
307328
return isNotNull != null;
308329
}

nullaway/src/test/java/com/uber/nullaway/AssertionLibsTests.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,4 +526,27 @@ public void doNotSupportAssertJAssertThatWhenDisabled() {
526526
"}")
527527
.doTest();
528528
}
529+
530+
@Test
531+
public void collectionAssertIsNotNull() {
532+
makeTestHelperWithArgs(
533+
Arrays.asList(
534+
"-d",
535+
temporaryFolder.getRoot().getAbsolutePath(),
536+
"-XepOpt:NullAway:AnnotatedPackages=com.uber",
537+
"-XepOpt:NullAway:HandleTestAssertionLibraries=true"))
538+
.addSourceLines(
539+
"Test.java",
540+
"import org.jspecify.annotations.*;",
541+
"import java.util.Collection;",
542+
"import static org.assertj.core.api.Assertions.assertThat;",
543+
"@NullMarked",
544+
"class Test {",
545+
" void test(@Nullable Collection<String> c) {",
546+
" assertThat(c).isNotNull();",
547+
" c.size();",
548+
" }",
549+
"}")
550+
.doTest();
551+
}
529552
}

0 commit comments

Comments
 (0)