-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add nullability annotations and compile-time checks #4557
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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 static transitive org.apiguardian.api; | ||
requires static transitive org.jspecify; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
requires static
would make it optional, but we'd have to suppress warnings like these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
@@ -565,7 +566,7 @@ static boolean isWideningConversion(Class<?> sourceType, Class<?> targetType) { | |||
* @return the corresponding wrapper type or {@code null} if the | |||
* 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From my branch:
Lines 61 to 64 in 791778e
if (type == null) { // FIXME parameter of ReflectionUtils.getWrapperType should be @Nullable | |
return null; | |
} | |
Class<?> wrapperType = ReflectionUtils.getWrapperType(type); |
triggered this, which is somehow related to the following on main
:
Line 117 in 261c2f5
Class<?> targetTypeToUse = toWrapperType(targetType); |
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcphilipp IIUC, this eventually didn't happen due to uber/NullAway#1083, and you instead went for annotating the packages, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, thank you! Updated in df24efb
Overview
This PR uses JSpecify's annotations to annotate packages as
@NullMarked
which implies fields, parameters, return types etc. in those package are not nullable by default. Ifnull
is allowed, the@Nullable
annotation 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
null
is passed as a parameter where it isn't allowed.I hereby agree to the terms of the JUnit Contributor License Agreement.
Definition of Done
@API
annotations