Skip to content

Commit f2bafb2

Browse files
authored
Add support for parameter types with wildcards for JarInfer (#1107)
1 parent 2754c45 commit f2bafb2

File tree

5 files changed

+98
-7
lines changed

5 files changed

+98
-7
lines changed

jar-infer/jar-infer-lib/src/main/java/com/uber/nullaway/jarinfer/DefinitelyDerefedParamsDriver.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,9 @@ private static String getSourceLevelQualifiedTypeName(TypeReference typ) {
566566
* @return source-level qualified type name.
567567
*/
568568
private static String getSourceLevelQualifiedTypeName(String typeName) {
569+
if (isWildcard(typeName)) {
570+
return sourceLevelWildcardType(typeName);
571+
}
569572
if (!typeName.endsWith(";")) {
570573
// we need the semicolon since some of WALA's TypeSignature APIs expect it
571574
typeName = typeName + ";";
@@ -598,4 +601,22 @@ private static String getSourceLevelQualifiedTypeName(String typeName) {
598601
+ ">";
599602
}
600603
}
604+
605+
private static boolean isWildcard(String typeName) {
606+
char firstChar = typeName.charAt(0);
607+
return firstChar == '*' || firstChar == '+' || firstChar == '-';
608+
}
609+
610+
private static String sourceLevelWildcardType(String typeName) {
611+
switch (typeName.charAt(0)) {
612+
case '*':
613+
return "?";
614+
case '+':
615+
return "? extends " + getSourceLevelQualifiedTypeName(typeName.substring(1));
616+
case '-':
617+
return "? super " + getSourceLevelQualifiedTypeName(typeName.substring(1));
618+
default:
619+
throw new RuntimeException("unexpected wildcard type name" + typeName);
620+
}
621+
}
601622
}

jar-infer/jar-infer-lib/src/test/java/com/uber/nullaway/jarinfer/JarInferTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,33 @@ public void testMethodWithGenericParameter() throws Exception {
481481
"}");
482482
}
483483

484+
@Test
485+
public void wildcards() throws Exception {
486+
testTemplate(
487+
"wildcards",
488+
"generic",
489+
"TestGeneric",
490+
ImmutableMap.of(
491+
"generic.TestGeneric:void genericWildcardLower(generic.TestGeneric.Generic<? super java.lang.String>)",
492+
Sets.newHashSet(0),
493+
"generic.TestGeneric:void genericWildcard(generic.TestGeneric.Generic<?>)",
494+
Sets.newHashSet(0),
495+
"generic.TestGeneric:java.lang.String genericWildcardUpper(generic.TestGeneric.Generic<? extends java.lang.String>)",
496+
Sets.newHashSet(0)),
497+
"public class TestGeneric {",
498+
" public abstract static class Generic<T> {",
499+
" public String getString(T t) {",
500+
" return \"t\";",
501+
" }",
502+
" public void doNothing() {}",
503+
" public abstract T getSomething();",
504+
" }",
505+
" public static void genericWildcard(Generic<?> g) { g.doNothing(); };",
506+
" public static String genericWildcardUpper(Generic<? extends String> g) { return g.getSomething(); };",
507+
" public static void genericWildcardLower(Generic<? super String> g) { g.getString(\"hello\"); };",
508+
"}");
509+
}
510+
484511
@Test
485512
public void toyJARAnnotatingClasses() throws Exception {
486513
testAnnotationInJarTemplate(

jar-infer/nullaway-integration-test/src/test/java/com/uber/nullaway/jarinfer/JarInferIntegrationTest.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,7 @@ public void genericsTest() {
8484
"import org.jspecify.annotations.Nullable;",
8585
"import com.uber.nullaway.jarinfer.toys.unannotated.Toys;",
8686
"class Test {",
87-
" void test1() {",
88-
" Toys.Generic<String> g = new Toys.Generic<>();",
87+
" void test1(Toys.Generic<String> g) {",
8988
" // BUG: Diagnostic contains: passing @Nullable parameter 'null'",
9089
" g.getString(null);",
9190
" // BUG: Diagnostic contains: passing @Nullable parameter 'null'",
@@ -95,6 +94,34 @@ public void genericsTest() {
9594
.doTest();
9695
}
9796

97+
@Test
98+
public void wildcards() {
99+
compilationHelper
100+
.setArgs(
101+
Arrays.asList(
102+
"-d",
103+
temporaryFolder.getRoot().getAbsolutePath(),
104+
"-XepOpt:NullAway:AnnotatedPackages=com.uber",
105+
"-XepOpt:NullAway:JarInferEnabled=true",
106+
"-XepOpt:NullAway:UnannotatedSubPackages=com.uber.nullaway.[a-zA-Z0-9.]+.unannotated"))
107+
.addSourceLines(
108+
"Test.java",
109+
"package com.uber;",
110+
"import org.jspecify.annotations.Nullable;",
111+
"import com.uber.nullaway.jarinfer.toys.unannotated.Toys;",
112+
"class Test {",
113+
" void test1() {",
114+
" // BUG: Diagnostic contains: passing @Nullable parameter 'null'",
115+
" Toys.genericWildcard(null);",
116+
" // BUG: Diagnostic contains: passing @Nullable parameter 'null'",
117+
" Toys.genericWildcardUpper(null);",
118+
" // BUG: Diagnostic contains: passing @Nullable parameter 'null'",
119+
" Toys.genericWildcardLower(null);",
120+
" }",
121+
"}")
122+
.doTest();
123+
}
124+
98125
@Test
99126
public void jarinferNullableReturnsTest() {
100127
compilationHelper

jar-infer/test-java-lib-jarinfer/src/main/java/com/uber/nullaway/jarinfer/toys/unannotated/Toys.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,32 @@ public static int testArray(Object[] o) {
3737
return o.hashCode();
3838
}
3939

40-
public static class Generic<T> {
40+
public abstract static class Generic<T> {
4141
public String getString(T t) {
4242
return t.toString();
4343
}
44+
45+
public void doNothing() {}
46+
47+
public abstract T getSomething();
4448
}
4549

4650
public static void genericParam(Generic<String> g) {
4751
g.getString("hello");
4852
}
4953

54+
public static void genericWildcard(Generic<?> g) {
55+
g.doNothing();
56+
}
57+
58+
public static String genericWildcardUpper(Generic<? extends String> g) {
59+
return g.getSomething();
60+
}
61+
62+
public static void genericWildcardLower(Generic<? super String> g) {
63+
g.getString("hello");
64+
}
65+
5066
public static void main(String arg[]) throws java.io.IOException {
5167
String s = "test string...";
5268
Foo f = new Foo("let's");

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,8 +1370,8 @@ public ImmutableSetMultimap<MethodRef, Integer> explicitlyNullableParameters() {
13701370
for (Map.Entry<Integer, Set<String>> entry : innerEntry.getValue().entrySet()) {
13711371
Integer index = entry.getKey();
13721372
if (index >= 0 && entry.getValue().stream().anyMatch(a -> a.contains("Nullable"))) {
1373-
// remove spaces
1374-
methodNameAndSignature = methodNameAndSignature.replaceAll("\\s", "");
1373+
// remove spaces after commas
1374+
methodNameAndSignature = methodNameAndSignature.replaceAll(",\\s", ",");
13751375
mapBuilder.put(methodRef(className, methodNameAndSignature), index);
13761376
}
13771377
}
@@ -1403,8 +1403,8 @@ public ImmutableSetMultimap<MethodRef, Integer> nonNullParameters() {
14031403
+ methodEntry.getKey()
14041404
+ " arg "
14051405
+ argEntry.getKey());
1406-
// remove spaces
1407-
methodNameAndSignature = methodNameAndSignature.replaceAll("\\s", "");
1406+
// remove spaces after commas
1407+
methodNameAndSignature = methodNameAndSignature.replaceAll(",\\s", ",");
14081408
mapBuilder.put(methodRef(className, methodNameAndSignature), index);
14091409
}
14101410
}

0 commit comments

Comments
 (0)