JSpecify mode: an override of a null-unmarked method is reported for mismatched type argument nullability
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 72/100
Research direction
Start by reading the JSpecify override checks and the existing tests around #2052 and #2074; the issue gives a CompilationTestHelper reproducer with four failing cases. Add coverage for the unmarked interfaces and verify those overrides produce no warnings, while the issue notes that explicitly annotated types in unmarked methods should remain checked. The work is done when the reproducer passes and the stated @NullMarked interface cases still report warnings.
Written by the indexing model from the issue text.
Description
In JSpecify mode, a class in @NullMarked code that implements a null-unmarked interface and declares List<@Nullable String> where the interface method has List<String> is reported with mismatched type parameter nullability. This happens for the return type and for a parameter, in a named class, in an anonymous class, and for an explicitly typed lambda parameter. The type argument String in the unmarked interface has unspecified nullness, so under the JSpecify "some world" rules List<@Nullable String> is a valid override and nothing should be reported.
Version: master at 2fd31d501 (0.14.4-SNAPSHOT), JDK 21.0.9.
Options: -XepOpt:NullAway:AnnotatedPackages=com.uber, plus the JSpecify mode arguments from JSpecifyJavacConfig.withJSpecifyModeArgs.
Reproducer (a CompilationTestHelper test extending NullAwayTestsBase):
// UnmarkedListSupplier.java
package com.example.unmarked;
import java.util.List;
public interface UnmarkedListSupplier {
List<String> get();
}
// UnmarkedListConsumer.java
package com.example.unmarked;
import java.util.List;
public interface UnmarkedListConsumer {
void accept(List<String> l);
}
// Test.java
package com.uber;
import com.example.unmarked.UnmarkedListConsumer;
import com.example.unmarked.UnmarkedListSupplier;
import java.util.List;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
class Test {
static class NamedSupplier implements UnmarkedListSupplier {
@Override
public List<@Nullable String> get() { throw new RuntimeException(); }
}
static class NamedConsumer implements UnmarkedListConsumer {
@Override
public void accept(List<@Nullable String> l) {}
}
void anonymous() {
UnmarkedListSupplier s = new UnmarkedListSupplier() {
@Override
public List<@Nullable String> get() { throw new RuntimeException(); }
};
}
void lambdaParam() {
UnmarkedListConsumer c = (List<@Nullable String> l) -> {};
}
}
Actual: four warnings:
/Test.java:11: warning: [NullAway] Method returns List<@Nullable String>, but overridden method returns List<String>, which has mismatched type parameter nullability
/Test.java:15: warning: [NullAway] Parameter has type List<@Nullable String>, but overridden method has parameter type List<String>, which has mismatched type parameter nullability
/Test.java:20: warning: [NullAway] Method returns List<@Nullable String>, but overridden method returns List<String>, which has mismatched type parameter nullability
/Test.java:24: warning: [NullAway] Parameter has type List<@Nullable String>, but overridden method has parameter type List<String>, which has mismatched type parameter nullability
Expected: no warnings in Test.java. With the interfaces annotated @NullMarked, all four lines should still be reported.
Type arguments of an unmarked method are already treated as unspecified elsewhere: arguments of a call to an unmarked method are not checked, a method reference passed for an unmarked functional interface is not checked since #2074, and #2052 skips the check for lambda returns. This came up in the review of #2052: https://github.com/uber/NullAway/pull/2052#discussion_r4368571361.
As with #2052, skipping the comparison would also leave explicit annotations in the unmarked method, such as List<@NonNull String>, unchecked.
- Dominant language
- Java
- Stars
- 4.1k
- Forks
- 381
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 106
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from uber/NullAway
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
uber/NullAway#2145 · 1 reaction ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 1-3 hours Newbie friendliness 85/100
uber/NullAway#2058 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
uber/NullAway#1899 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 15/100
uber/NullAway#2160 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 62/100
Maintainers usually reply within 1 day
Similar issues
-
Cleanup: restore the interrupt flag in AiFailureAnalyzerPossibly taken @Rishankgupta08 claimed this today. Opengood first issue
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
seleniumboot/selenium-boot#258 · 1 comment ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
dense-analysis/ale#5181 ·
-
bug subsystem:agent-harness triage
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
ShaftHQ/SHAFT_ENGINE#8043 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 88/100
johanhaleby/occurrent#1505 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day