-
Notifications
You must be signed in to change notification settings - Fork 296
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
JSpecify: read upper bound annotations from bytecode and add tests (#…
…1004) We add support for reading upper bound annotations from bytecodes for JSpecify mode generics checks. Many more types of annotations need to be read with special handling on JDK versions earlier than 22, due to https://bugs.openjdk.org/browse/JDK-8225377. For now, we disable some tests on earlier JDKs. We may add this special handling in the future, but it will be a significant amount of work. We are still hoping the relevant javac fix gets backported, which will fix this for us; see jspecify/jspecify#365.
- Loading branch information
Showing
8 changed files
with
318 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
230 changes: 230 additions & 0 deletions
230
nullaway/src/test/java/com/uber/nullaway/jspecify/BytecodeGenericsTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
package com.uber.nullaway.jspecify; | ||
|
||
import com.google.errorprone.CompilationTestHelper; | ||
import com.uber.nullaway.NullAwayTestsBase; | ||
import java.util.Arrays; | ||
import org.junit.Test; | ||
|
||
public class BytecodeGenericsTests extends NullAwayTestsBase { | ||
|
||
@Test | ||
public void basicTypeParamInstantiation() { | ||
makeHelper() | ||
.addSourceLines( | ||
"Test.java", | ||
"package com.uber;", | ||
"import org.jspecify.annotations.Nullable;", | ||
"import com.uber.lib.generics.NonNullTypeParam;", | ||
"import com.uber.lib.generics.NullableTypeParam;", | ||
"class Test {", | ||
" // BUG: Diagnostic contains: Generic type parameter", | ||
" static void testBadNonNull(NonNullTypeParam<@Nullable String> t1) {", | ||
" // BUG: Diagnostic contains: Generic type parameter", | ||
" NonNullTypeParam<@Nullable String> t2 = null;", | ||
" NullableTypeParam<@Nullable String> t3 = null;", | ||
" }", | ||
"}") | ||
.doTest(); | ||
} | ||
|
||
@Test | ||
public void multipleTypeParametersInstantiation() { | ||
makeHelper() | ||
.addSourceLines( | ||
"Test.java", | ||
"package com.uber;", | ||
"import org.jspecify.annotations.Nullable;", | ||
"import com.uber.lib.generics.MixedTypeParam;", | ||
"class Test {", | ||
" static class PartiallyInvalidSubclass", | ||
" // BUG: Diagnostic contains: Generic type parameter", | ||
" extends MixedTypeParam<@Nullable String, String, String, @Nullable String> {}", | ||
" static class ValidSubclass1", | ||
" extends MixedTypeParam<String, @Nullable String, @Nullable String, String> {}", | ||
" static class PartiallyInvalidSubclass2", | ||
" extends MixedTypeParam<", | ||
" String,", | ||
" String,", | ||
" String,", | ||
" // BUG: Diagnostic contains: Generic type parameter", | ||
" @Nullable String> {}", | ||
" static class ValidSubclass2 extends MixedTypeParam<String, String, String, String> {}", | ||
"}") | ||
.doTest(); | ||
} | ||
|
||
@Test | ||
public void genericsChecksForAssignments() { | ||
makeHelper() | ||
.addSourceLines( | ||
"Test.java", | ||
"package com.uber;", | ||
"import org.jspecify.annotations.Nullable;", | ||
"import com.uber.lib.generics.NullableTypeParam;", | ||
"class Test {", | ||
" static void testPositive(NullableTypeParam<@Nullable String> t1) {", | ||
" // BUG: Diagnostic contains: Cannot assign from type NullableTypeParam<@Nullable String>", | ||
" NullableTypeParam<String> t2 = t1;", | ||
" }", | ||
" static void testNegative(NullableTypeParam<@Nullable String> t1) {", | ||
" NullableTypeParam<@Nullable String> t2 = t1;", | ||
" }", | ||
"}") | ||
.doTest(); | ||
} | ||
|
||
@Test | ||
public void genericsChecksForFieldAssignments() { | ||
makeHelper() | ||
.addSourceLines( | ||
"Test.java", | ||
"package com.uber;", | ||
"import org.jspecify.annotations.Nullable;", | ||
"import com.uber.lib.generics.NullableTypeParam;", | ||
"class Test {", | ||
" static void testPositive(NullableTypeParam<String> t1) {", | ||
" // BUG: Diagnostic contains: Cannot assign from type NullableTypeParam<String>", | ||
" NullableTypeParam.staticField = t1;", | ||
" // BUG: Diagnostic contains: Cannot assign from type NullableTypeParam<@Nullable String>", | ||
" NullableTypeParam<String> t2 = NullableTypeParam.staticField;", | ||
" }", | ||
" static void testNegative(NullableTypeParam<@Nullable String> t1) {", | ||
" NullableTypeParam.staticField = t1;", | ||
" NullableTypeParam<@Nullable String> t2 = NullableTypeParam.staticField;", | ||
" }", | ||
"}") | ||
.doTest(); | ||
} | ||
|
||
@Test | ||
public void genericsChecksForParamPassingAndReturns() { | ||
makeHelper() | ||
.addSourceLines( | ||
"Test.java", | ||
"package com.uber;", | ||
"import org.jspecify.annotations.Nullable;", | ||
"import com.uber.lib.generics.NullableTypeParam;", | ||
"import com.uber.lib.generics.GenericTypeArgMethods;", | ||
"class Test {", | ||
" static void testPositive(NullableTypeParam<String> t1) {", | ||
" // BUG: Diagnostic contains: Cannot pass parameter of type NullableTypeParam<String>", | ||
" GenericTypeArgMethods.nullableTypeParamArg(t1);", | ||
" // BUG: Diagnostic contains: Cannot assign from type NullableTypeParam<@Nullable String>", | ||
" NullableTypeParam<String> t2 = GenericTypeArgMethods.nullableTypeParamReturn();", | ||
" }", | ||
" static void testNegative(NullableTypeParam<@Nullable String> t1) {", | ||
" GenericTypeArgMethods.nullableTypeParamArg(t1);", | ||
" NullableTypeParam<@Nullable String> t2 = GenericTypeArgMethods.nullableTypeParamReturn();", | ||
" }", | ||
"}") | ||
.doTest(); | ||
} | ||
|
||
@Test | ||
public void overrideParameterType() { | ||
makeHelper() | ||
.addSourceLines( | ||
"Test.java", | ||
"package com.uber;", | ||
"import org.jspecify.annotations.Nullable;", | ||
"import com.uber.lib.generics.Fn;", | ||
"class Test {", | ||
" static class TestFunc1 implements Fn<@Nullable String, String> {", | ||
" @Override", | ||
" // BUG: Diagnostic contains: parameter s is", | ||
" public String apply(String s) {", | ||
" return s;", | ||
" }", | ||
" }", | ||
" static class TestFunc2 implements Fn<@Nullable String, String> {", | ||
" @Override", | ||
" public String apply(@Nullable String s) {", | ||
" return \"hi\";", | ||
" }", | ||
" }", | ||
" static class TestFunc3 implements Fn<String, String> {", | ||
" @Override", | ||
" public String apply(String s) {", | ||
" return \"hi\";", | ||
" }", | ||
" }", | ||
" static class TestFunc4 implements Fn<String, String> {", | ||
" // this override is legal, we should get no error", | ||
" @Override", | ||
" public String apply(@Nullable String s) {", | ||
" return \"hi\";", | ||
" }", | ||
" }", | ||
" static void useTestFunc(String s) {", | ||
" Fn<@Nullable String, String> f1 = new TestFunc2();", | ||
" // should get no error here", | ||
" f1.apply(null);", | ||
" Fn<String, String> f2 = new TestFunc3();", | ||
" // BUG: Diagnostic contains: passing @Nullable parameter", | ||
" f2.apply(null);", | ||
" }", | ||
"}") | ||
.doTest(); | ||
} | ||
|
||
@Test | ||
public void overrideReturnTypes() { | ||
makeHelper() | ||
.addSourceLines( | ||
"Test.java", | ||
"package com.uber;", | ||
"import org.jspecify.annotations.Nullable;", | ||
"import com.uber.lib.generics.Fn;", | ||
"class Test {", | ||
" static class TestFunc1 implements Fn<String, @Nullable String> {", | ||
" @Override", | ||
" public @Nullable String apply(String s) {", | ||
" return s;", | ||
" }", | ||
" }", | ||
" static class TestFunc2 implements Fn<String, @Nullable String> {", | ||
" @Override", | ||
" public String apply(String s) {", | ||
" return s;", | ||
" }", | ||
" }", | ||
" static class TestFunc3 implements Fn<String, String> {", | ||
" @Override", | ||
" // BUG: Diagnostic contains: method returns @Nullable, but superclass", | ||
" public @Nullable String apply(String s) {", | ||
" return s;", | ||
" }", | ||
" }", | ||
" static class TestFunc4 implements Fn<@Nullable String, String> {", | ||
" @Override", | ||
" // BUG: Diagnostic contains: method returns @Nullable, but superclass", | ||
" public @Nullable String apply(String s) {", | ||
" return s;", | ||
" }", | ||
" }", | ||
" static void useTestFunc(String s) {", | ||
" Fn<String, @Nullable String> f1 = new TestFunc1();", | ||
" String t1 = f1.apply(s);", | ||
" // BUG: Diagnostic contains: dereferenced expression", | ||
" t1.hashCode();", | ||
" TestFunc2 f2 = new TestFunc2();", | ||
" String t2 = f2.apply(s);", | ||
" // There should not be an error here", | ||
" t2.hashCode();", | ||
" Fn<String, @Nullable String> f3 = new TestFunc2();", | ||
" String t3 = f3.apply(s);", | ||
" // BUG: Diagnostic contains: dereferenced expression", | ||
" t3.hashCode();", | ||
" // BUG: Diagnostic contains: dereferenced expression", | ||
" f3.apply(s).hashCode();", | ||
" }", | ||
"}") | ||
.doTest(); | ||
} | ||
|
||
private CompilationTestHelper makeHelper() { | ||
return makeTestHelperWithArgs( | ||
Arrays.asList( | ||
"-XepOpt:NullAway:AnnotatedPackages=com.uber", "-XepOpt:NullAway:JSpecifyMode=true")); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.uber.lib.generics; | ||
|
||
import org.jspecify.annotations.Nullable; | ||
|
||
public interface Fn<P extends @Nullable Object, R extends @Nullable Object> { | ||
R apply(P p); | ||
} |
12 changes: 12 additions & 0 deletions
12
test-java-lib/src/main/java/com/uber/lib/generics/GenericTypeArgMethods.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.uber.lib.generics; | ||
|
||
import org.jspecify.annotations.Nullable; | ||
|
||
public class GenericTypeArgMethods { | ||
|
||
public static void nullableTypeParamArg(NullableTypeParam<@Nullable String> s) {} | ||
|
||
public static NullableTypeParam<@Nullable String> nullableTypeParamReturn() { | ||
return new NullableTypeParam<@Nullable String>(); | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
test-java-lib/src/main/java/com/uber/lib/generics/MixedTypeParam.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.uber.lib.generics; | ||
|
||
import org.jspecify.annotations.Nullable; | ||
|
||
public class MixedTypeParam<E1, E2 extends @Nullable Object, E3 extends @Nullable Object, E4> {} |
3 changes: 3 additions & 0 deletions
3
test-java-lib/src/main/java/com/uber/lib/generics/NonNullTypeParam.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package com.uber.lib.generics; | ||
|
||
public class NonNullTypeParam<E> {} |
9 changes: 9 additions & 0 deletions
9
test-java-lib/src/main/java/com/uber/lib/generics/NullableTypeParam.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.uber.lib.generics; | ||
|
||
import org.jspecify.annotations.Nullable; | ||
|
||
public class NullableTypeParam<E extends @Nullable Object> { | ||
|
||
public static NullableTypeParam<@Nullable String> staticField = | ||
new NullableTypeParam<@Nullable String>(); | ||
} |