Add nullability annotations and compile-time checks#4557
Conversation
Would it make sense to annotate modules instead of packages with |
f61aa87 to
f8f1e3d
Compare
Yes, I think that's a good idea! I went package by package but when a module is done moving the annotation to the module declaration makes sense. 👍 I'll give that a shot. |
|
Unfortunately, NullAway does not support that: |
f8f1e3d to
e2cd369
Compare
|
I think a found a good "interim" solution:
|
| requires java.management; // needed by RuntimeUtils to determine input arguments | ||
|
|
||
| requires static transitive org.apiguardian.api; | ||
| requires static transitive org.jspecify; |
There was a problem hiding this comment.
We should discuss whether this is the right "scope". It requires downstream projects to have the JSpecify jar on their module path (should they be compiled using the module path).
There was a problem hiding this comment.
requires static would make it optional, but we'd have to suppress warnings like these.
There was a problem hiding this comment.
Mh, yeah. It ships with a compiled module descriptor, though: org.jspecify
e950738 to
914277e
Compare
|
|
||
| tasks.withType<JavaCompile>().configureEach { | ||
| options.errorprone { | ||
| disableAllChecks = true |
There was a problem hiding this comment.
We can later (after this PR) enable additional checks provided by Error Prone but I didn't want to mix those changes into this already large set of changes.
7f62e96 to
907cdd0
Compare
|
I'll put annotations back on the packages after the discussion in uber/NullAway#1083 (comment). |
f3fca05 to
11adeb2
Compare
| @ArchTest | ||
| void packagesShouldBeNullMarked(JavaClasses classes) { | ||
| var exclusions = Stream.of( // | ||
| "..shadow..", // | ||
| "org.junit.jupiter.api..", // | ||
| "org.junit.jupiter.engine..", // | ||
| "org.junit.jupiter.migrationsupport..", // | ||
| "org.junit.jupiter.params..", // | ||
| "org.junit.platform.launcher.." // | ||
| ).map(PackageMatcher::of).toList(); | ||
|
|
||
| var subpackages = Stream.of("org.junit.platform", "org.junit.jupiter", "org.junit.vintage") // | ||
| .map(classes::getPackage) // | ||
| .flatMap(rootPackage -> rootPackage.getSubpackagesInTree().stream()) // | ||
| .filter(pkg -> exclusions.stream().noneMatch(it -> it.matches(pkg.getName()))) // | ||
| .filter(pkg -> !pkg.getClasses().isEmpty()) // | ||
| .toList(); | ||
| assertThat(subpackages).isNotEmpty(); | ||
|
|
||
| var violations = subpackages.stream() // | ||
| .filter(pkg -> !pkg.isAnnotatedWith(NullMarked.class)) // | ||
| .map(JavaPackage::getName) // | ||
| .sorted(); | ||
| assertThat(violations).describedAs("The following packages are missing the @NullMarked annotation").isEmpty(); | ||
| } |
There was a problem hiding this comment.
This check will help us to avoid forgetting to add the annotation on new packages.
cd8776e to
29ede48
Compare
29ede48 to
834e903
Compare
834e903 to
aa121ee
Compare
| * supplied type is {@code null} or not a primitive type | ||
| */ | ||
| public static Class<?> getWrapperType(Class<?> type) { | ||
| public static @Nullable Class<?> getWrapperType(Class<?> type) { |
There was a problem hiding this comment.
@marcphilipp given that the Javadoc mentions:
* @return the corresponding wrapper type or {@code null} if the
* supplied type is {@code null} or not a primitive type
should the parameter also have @Nullable?
public static @Nullable Class<?> getWrapperType(@Nullable Class<?> type)(I spotted it during the rebase of #4219)
After a quick look, I think there are no unit tests for getWrapperType() in ReflectionUtilsTests.
Happy to submit a PR to address both topics, if you agree 🙂
There was a problem hiding this comment.
Good catch! On main, there are no usages that potentially pass null, are there? Do you need that for #4219? If so, adding @Nullable along with tests sounds good to me. If not, I think we should rather change the Javadoc.
There was a problem hiding this comment.
From my branch:
triggered this, which is somehow related to the following on main:
I try to figure out why the warning doesn't happen on main 🤔
| - The descriptor of each module is annotated with `@NullMarked` for IDEs such as IntelliJ | ||
| IDEA to treat code correctly. |
There was a problem hiding this comment.
@marcphilipp IIUC, this eventually didn't happen due to uber/NullAway#1083, and you instead went for annotating the packages, right?
Overview
This PR uses JSpecify's annotations to annotate packages as
@NullMarkedwhich implies fields, parameters, return types etc. in those package are not nullable by default. Ifnullis allowed, the@Nullableannotation is used to indicate that.At compile-time Error Prone with the NullAway plugin is used to verify consistency of the annotations causing the build to fail, for example, if
nullis passed as a parameter where it isn't allowed.I hereby agree to the terms of the JUnit Contributor License Agreement.
Definition of Done
@APIannotations