Skip to content

Commit 4d67c0e

Browse files
committed
Enable NullAway's JSpecify mode and fix resulting issues
1 parent d0d6071 commit 4d67c0e

File tree

36 files changed

+129
-80
lines changed

36 files changed

+129
-80
lines changed

gradle/plugins/common/src/main/kotlin/junitbuild.java-nullability-conventions.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ tasks.withType<JavaCompile>().configureEach {
2222
disableAllChecks = true
2323
nullaway {
2424
enable()
25+
isJSpecifyMode = true
2526
}
2627
}
2728
}

junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/InvocationInterceptor.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public interface InvocationInterceptor extends TestInstantiationAwareExtension {
7878
* @return the result of the invocation; never {@code null}
7979
* @throws Throwable in case of failure
8080
*/
81-
default <T> T interceptTestClassConstructor(Invocation<T> invocation,
81+
default <T> @Nullable T interceptTestClassConstructor(Invocation<T> invocation,
8282
ReflectiveInvocationContext<Constructor<T>> invocationContext, ExtensionContext extensionContext)
8383
throws Throwable {
8484
return invocation.proceed();
@@ -94,7 +94,7 @@ default <T> T interceptTestClassConstructor(Invocation<T> invocation,
9494
* @param extensionContext the current extension context; never {@code null}
9595
* @throws Throwable in case of failures
9696
*/
97-
default void interceptBeforeAllMethod(Invocation<@Nullable Void> invocation,
97+
default void interceptBeforeAllMethod(Invocation<Void> invocation,
9898
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
9999
invocation.proceed();
100100
}
@@ -109,7 +109,7 @@ default void interceptBeforeAllMethod(Invocation<@Nullable Void> invocation,
109109
* @param extensionContext the current extension context; never {@code null}
110110
* @throws Throwable in case of failures
111111
*/
112-
default void interceptBeforeEachMethod(Invocation<@Nullable Void> invocation,
112+
default void interceptBeforeEachMethod(Invocation<Void> invocation,
113113
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
114114
invocation.proceed();
115115
}
@@ -124,8 +124,8 @@ default void interceptBeforeEachMethod(Invocation<@Nullable Void> invocation,
124124
* @param extensionContext the current extension context; never {@code null}
125125
* @throws Throwable in case of failures
126126
*/
127-
default void interceptTestMethod(Invocation<@Nullable Void> invocation,
128-
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
127+
default void interceptTestMethod(Invocation<Void> invocation, ReflectiveInvocationContext<Method> invocationContext,
128+
ExtensionContext extensionContext) throws Throwable {
129129
invocation.proceed();
130130
}
131131

@@ -143,7 +143,7 @@ default void interceptTestMethod(Invocation<@Nullable Void> invocation,
143143
* @return the result of the invocation; potentially {@code null}
144144
* @throws Throwable in case of failures
145145
*/
146-
default <T extends @Nullable Object> T interceptTestFactoryMethod(Invocation<T> invocation,
146+
default <T> @Nullable T interceptTestFactoryMethod(Invocation<T> invocation,
147147
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
148148
return invocation.proceed();
149149
}
@@ -158,7 +158,7 @@ default void interceptTestMethod(Invocation<@Nullable Void> invocation,
158158
* @param extensionContext the current extension context; never {@code null}
159159
* @throws Throwable in case of failures
160160
*/
161-
default void interceptTestTemplateMethod(Invocation<@Nullable Void> invocation,
161+
default void interceptTestTemplateMethod(Invocation<Void> invocation,
162162
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
163163
invocation.proceed();
164164
}
@@ -174,8 +174,8 @@ default void interceptTestTemplateMethod(Invocation<@Nullable Void> invocation,
174174
* @throws Throwable in case of failures
175175
*/
176176
@API(status = STABLE, since = "5.11")
177-
default void interceptDynamicTest(Invocation<@Nullable Void> invocation,
178-
DynamicTestInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable {
177+
default void interceptDynamicTest(Invocation<Void> invocation, DynamicTestInvocationContext invocationContext,
178+
ExtensionContext extensionContext) throws Throwable {
179179
invocation.proceed();
180180
}
181181

@@ -189,7 +189,7 @@ default void interceptDynamicTest(Invocation<@Nullable Void> invocation,
189189
* @param extensionContext the current extension context; never {@code null}
190190
* @throws Throwable in case of failures
191191
*/
192-
default void interceptAfterEachMethod(Invocation<@Nullable Void> invocation,
192+
default void interceptAfterEachMethod(Invocation<Void> invocation,
193193
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
194194
invocation.proceed();
195195
}
@@ -204,7 +204,7 @@ default void interceptAfterEachMethod(Invocation<@Nullable Void> invocation,
204204
* @param extensionContext the current extension context; never {@code null}
205205
* @throws Throwable in case of failures
206206
*/
207-
default void interceptAfterAllMethod(Invocation<@Nullable Void> invocation,
207+
default void interceptAfterAllMethod(Invocation<Void> invocation,
208208
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
209209
invocation.proceed();
210210
}
@@ -218,14 +218,15 @@ default void interceptAfterAllMethod(Invocation<@Nullable Void> invocation,
218218
* @since 5.5
219219
*/
220220
@API(status = STABLE, since = "5.10")
221-
interface Invocation<T extends @Nullable Object> {
221+
interface Invocation<T> {
222222

223223
/**
224224
* Proceed with this invocation.
225225
*
226226
* @return the result of this invocation; potentially {@code null}.
227227
* @throws Throwable in case the invocation failed
228228
*/
229+
@Nullable
229230
T proceed() throws Throwable;
230231

231232
/**

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassBasedTestDescriptor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,8 +409,8 @@ private Object invokeTestClassConstructor(Optional<Object> outerInstance, Extens
409409
ExtensionContextSupplier extensionContext) {
410410

411411
Constructor<?> constructor = ReflectionUtils.getDeclaredConstructor(getTestClass());
412-
return executableInvoker.invoke(constructor, outerInstance, extensionContext, registry,
413-
InvocationInterceptor::interceptTestClassConstructor);
412+
return requireNonNull(executableInvoker.invoke(constructor, outerInstance, extensionContext, registry,
413+
InvocationInterceptor::interceptTestClassConstructor));
414414
}
415415

416416
private void invokeTestInstancePreConstructCallbacks(TestInstanceFactoryContext factoryContext,

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicTestTestDescriptor.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ public JupiterEngineExecutionContext execute(JupiterEngineExecutionContext conte
7171
return context;
7272
}
7373

74-
@SuppressWarnings("NullAway")
7574
private InvocationInterceptor.Invocation<Void> toInvocation() {
7675
return () -> {
7776
requiredDynamicTest().getExecutable().execute();

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public class TestFactoryTestDescriptor extends TestMethodTestDescriptor implemen
6060
public static final String DYNAMIC_TEST_SEGMENT_TYPE = "dynamic-test";
6161

6262
@SuppressWarnings("NullAway")
63-
private static final ReflectiveInterceptorCall<Method, @Nullable Object> interceptorCall = InvocationInterceptor::interceptTestFactoryMethod;
63+
private static final ReflectiveInterceptorCall<Method, Object> interceptorCall = InvocationInterceptor::interceptTestFactoryMethod;
6464
private static final InterceptingExecutableInvoker executableInvoker = new InterceptingExecutableInvoker();
6565

6666
private final DynamicDescendantFilter dynamicDescendantFilter;

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvoker.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,14 @@ public class InterceptingExecutableInvoker {
5454
* invocation via all registered {@linkplain InvocationInterceptor
5555
* interceptors}
5656
*/
57-
public <T> T invoke(Constructor<T> constructor, Optional<Object> outerInstance,
57+
public <T> @Nullable T invoke(Constructor<T> constructor, Optional<Object> outerInstance,
5858
ExtensionContextSupplier extensionContext, ExtensionRegistry extensionRegistry,
5959
ReflectiveInterceptorCall<Constructor<T>, T> interceptorCall) {
6060

6161
@Nullable
6262
Object[] arguments = resolveParameters(constructor, Optional.empty(), outerInstance, extensionContext,
6363
extensionRegistry);
64-
ConstructorInvocation<T> invocation = new ConstructorInvocation<>(constructor, arguments);
64+
ConstructorInvocation<T> invocation = newConstructorInvocation(constructor, arguments);
6565
return invoke(invocation, invocation, extensionContext, extensionRegistry, interceptorCall);
6666
}
6767

@@ -78,40 +78,53 @@ public <T> T invoke(Constructor<T> constructor, Optional<Object> outerInstance,
7878
* @param interceptorCall the call for intercepting this method invocation
7979
* via all registered {@linkplain InvocationInterceptor interceptors}
8080
*/
81-
public <T> T invoke(Method method, @Nullable Object target, ExtensionContext extensionContext,
81+
public <T> @Nullable T invoke(Method method, @Nullable Object target, ExtensionContext extensionContext,
8282
ExtensionRegistry extensionRegistry, ReflectiveInterceptorCall<Method, T> interceptorCall) {
8383

8484
@SuppressWarnings({ "unchecked", "rawtypes" })
8585
Optional<Object> optionalTarget = (target instanceof Optional optional ? optional
8686
: Optional.ofNullable(target));
8787
@Nullable
8888
Object[] arguments = resolveParameters(method, optionalTarget, extensionContext, extensionRegistry);
89-
MethodInvocation<T> invocation = new MethodInvocation<>(method, optionalTarget, arguments);
89+
MethodInvocation<T> invocation = newMethodInvocation(method, optionalTarget, arguments);
9090
return invoke(invocation, invocation, extensionContext, extensionRegistry, interceptorCall);
9191
}
9292

93-
private <E extends Executable, T> T invoke(Invocation<T> originalInvocation,
93+
private <E extends Executable, T extends @Nullable Object> @Nullable T invoke(Invocation<T> originalInvocation,
9494
ReflectiveInvocationContext<E> invocationContext, ExtensionContext extensionContext,
9595
ExtensionRegistry extensionRegistry, ReflectiveInterceptorCall<E, T> call) {
9696
return interceptorChain.invoke(originalInvocation, extensionRegistry, (interceptor,
9797
wrappedInvocation) -> call.apply(interceptor, wrappedInvocation, invocationContext, extensionContext));
9898
}
9999

100-
private <E extends Executable, T> T invoke(Invocation<T> originalInvocation,
100+
private <E extends Executable, T> @Nullable T invoke(Invocation<T> originalInvocation,
101101
ReflectiveInvocationContext<E> invocationContext, ExtensionContextSupplier extensionContext,
102102
ExtensionRegistry extensionRegistry, ReflectiveInterceptorCall<E, T> call) {
103103
return interceptorChain.invoke(originalInvocation, extensionRegistry,
104104
(interceptor, wrappedInvocation) -> call.apply(interceptor, wrappedInvocation, invocationContext,
105105
extensionContext.get(interceptor)));
106106
}
107107

108-
public interface ReflectiveInterceptorCall<E extends Executable, T extends @Nullable Object> {
108+
@SuppressWarnings("NullAway")
109+
private static <T> ConstructorInvocation<T> newConstructorInvocation(Constructor<T> constructor,
110+
@Nullable Object[] arguments) {
111+
return new ConstructorInvocation<>(constructor, arguments);
112+
}
113+
114+
@SuppressWarnings("NullAway")
115+
private static <T> MethodInvocation<T> newMethodInvocation(Method method, Optional<Object> optionalTarget,
116+
@Nullable Object[] arguments) {
117+
return new MethodInvocation<>(method, optionalTarget, arguments);
118+
}
109119

120+
public interface ReflectiveInterceptorCall<E extends Executable, T> {
121+
122+
@Nullable
110123
T apply(InvocationInterceptor interceptor, Invocation<T> invocation,
111124
ReflectiveInvocationContext<E> invocationContext, ExtensionContext extensionContext) throws Throwable;
112125

113126
@SuppressWarnings("NullAway")
114-
static ReflectiveInterceptorCall<Method, @Nullable Void> ofVoidMethod(VoidMethodInterceptorCall call) {
127+
static ReflectiveInterceptorCall<Method, Void> ofVoidMethod(VoidMethodInterceptorCall call) {
115128
return ((interceptorChain, invocation, invocationContext, extensionContext) -> {
116129
call.apply(interceptorChain, invocation, invocationContext, extensionContext);
117130
return null;

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InvocationInterceptorChain.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,16 @@
3030
@API(status = INTERNAL, since = "5.5")
3131
public class InvocationInterceptorChain {
3232

33-
public <T> T invoke(Invocation<T> invocation, ExtensionRegistry extensionRegistry, InterceptorCall<T> call) {
33+
public <T> @Nullable T invoke(Invocation<T> invocation, ExtensionRegistry extensionRegistry,
34+
InterceptorCall<T> call) {
3435
List<InvocationInterceptor> interceptors = extensionRegistry.getExtensions(InvocationInterceptor.class);
3536
if (interceptors.isEmpty()) {
3637
return proceed(invocation);
3738
}
3839
return chainAndInvoke(invocation, call, interceptors);
3940
}
4041

41-
private <T> T chainAndInvoke(Invocation<T> invocation, InterceptorCall<T> call,
42+
private <T> @Nullable T chainAndInvoke(Invocation<T> invocation, InterceptorCall<T> call,
4243
List<InvocationInterceptor> interceptors) {
4344

4445
ValidatingInvocation<T> validatingInvocation = new ValidatingInvocation<>(invocation, interceptors);
@@ -60,7 +61,7 @@ private <T> Invocation<T> chainInterceptors(Invocation<T> invocation, Intercepto
6061
return result;
6162
}
6263

63-
private <T> T proceed(Invocation<T> invocation) {
64+
private <T> @Nullable T proceed(Invocation<T> invocation) {
6465
try {
6566
return invocation.proceed();
6667
}
@@ -70,12 +71,13 @@ private <T> T proceed(Invocation<T> invocation) {
7071
}
7172

7273
@FunctionalInterface
73-
public interface InterceptorCall<T extends @Nullable Object> {
74+
public interface InterceptorCall<T> {
7475

76+
@Nullable
7577
T apply(InvocationInterceptor interceptor, Invocation<T> invocation) throws Throwable;
7678

7779
@SuppressWarnings("NullAway")
78-
static InterceptorCall<@Nullable Void> ofVoid(VoidInterceptorCall call) {
80+
static InterceptorCall<Void> ofVoid(VoidInterceptorCall call) {
7981
return ((interceptorChain, invocation) -> {
8082
call.apply(interceptorChain, invocation);
8183
return null;
@@ -87,15 +89,15 @@ public interface InterceptorCall<T extends @Nullable Object> {
8789
@FunctionalInterface
8890
public interface VoidInterceptorCall {
8991

90-
void apply(InvocationInterceptor interceptor, Invocation<@Nullable Void> invocation) throws Throwable;
92+
void apply(InvocationInterceptor interceptor, Invocation<Void> invocation) throws Throwable;
9193

9294
}
9395

9496
private record InterceptedInvocation<T>(Invocation<T> invocation, InterceptorCall<T> call,
9597
InvocationInterceptor interceptor) implements Invocation<T> {
9698

9799
@Override
98-
public T proceed() throws Throwable {
100+
public @Nullable T proceed() throws Throwable {
99101
return call.apply(interceptor, invocation);
100102
}
101103

@@ -119,7 +121,7 @@ private static class ValidatingInvocation<T extends @Nullable Object> implements
119121
}
120122

121123
@Override
122-
public T proceed() throws Throwable {
124+
public @Nullable T proceed() throws Throwable {
123125
markInvokedOrSkipped();
124126
return delegate.proceed();
125127
}

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/NamespaceAwareStore.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,30 @@ public NamespaceAwareStore(NamespacedHierarchicalStore<Namespace> valuesStore, N
3939
}
4040

4141
@Override
42+
@SuppressWarnings("NullAway")
4243
public @Nullable Object get(Object key) {
4344
Preconditions.notNull(key, "key must not be null");
4445
return accessStore(() -> this.valuesStore.get(this.namespace, key));
4546
}
4647

4748
@Override
49+
@SuppressWarnings("NullAway")
4850
public <T> @Nullable T get(Object key, Class<T> requiredType) {
4951
Preconditions.notNull(key, "key must not be null");
5052
Preconditions.notNull(requiredType, "requiredType must not be null");
5153
return accessStore(() -> this.valuesStore.get(this.namespace, key, requiredType));
5254
}
5355

5456
@Override
57+
@SuppressWarnings("NullAway")
5558
public <K, V> @Nullable Object getOrComputeIfAbsent(K key, Function<K, V> defaultCreator) {
5659
Preconditions.notNull(key, "key must not be null");
5760
Preconditions.notNull(defaultCreator, "defaultCreator function must not be null");
5861
return accessStore(() -> this.valuesStore.getOrComputeIfAbsent(this.namespace, key, defaultCreator));
5962
}
6063

6164
@Override
65+
@SuppressWarnings("NullAway")
6266
public <K, V> @Nullable V getOrComputeIfAbsent(K key, Function<K, V> defaultCreator, Class<V> requiredType) {
6367
Preconditions.notNull(key, "key must not be null");
6468
Preconditions.notNull(defaultCreator, "defaultCreator function must not be null");
@@ -68,18 +72,21 @@ public NamespaceAwareStore(NamespacedHierarchicalStore<Namespace> valuesStore, N
6872
}
6973

7074
@Override
75+
@SuppressWarnings("NullAway")
7176
public void put(Object key, @Nullable Object value) {
7277
Preconditions.notNull(key, "key must not be null");
7378
accessStore(() -> this.valuesStore.put(this.namespace, key, value));
7479
}
7580

7681
@Override
82+
@SuppressWarnings("NullAway")
7783
public @Nullable Object remove(Object key) {
7884
Preconditions.notNull(key, "key must not be null");
7985
return accessStore(() -> this.valuesStore.remove(this.namespace, key));
8086
}
8187

8288
@Override
89+
@SuppressWarnings("NullAway")
8390
public <T> @Nullable T remove(Object key, Class<T> requiredType) {
8491
Preconditions.notNull(key, "key must not be null");
8592
Preconditions.notNull(requiredType, "requiredType must not be null");

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocation.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class SeparateThreadTimeoutInvocation<T extends @Nullable Object> implements Inv
3737
}
3838

3939
@Override
40+
@SuppressWarnings("NullAway")
4041
public T proceed() throws Throwable {
4142
return assertTimeoutPreemptively(timeout.toDuration(), delegate::proceed, descriptionSupplier,
4243
(__, messageSupplier, cause, testThread) -> {

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExtension.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public void interceptTestTemplateMethod(Invocation<Void> invocation,
103103
}
104104

105105
@Override
106-
public <T> T interceptTestFactoryMethod(Invocation<T> invocation,
106+
public <T> @Nullable T interceptTestFactoryMethod(Invocation<T> invocation,
107107
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {
108108

109109
return interceptTestableMethod(invocation, invocationContext, extensionContext,
@@ -145,7 +145,7 @@ private Optional<ThreadMode> readTimeoutThreadModeFromAnnotation(Optional<Annota
145145
return AnnotationSupport.findAnnotation(element, Timeout.class).map(Timeout::threadMode);
146146
}
147147

148-
private <T> T interceptTestableMethod(Invocation<T> invocation,
148+
private <T> @Nullable T interceptTestableMethod(Invocation<T> invocation,
149149
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext,
150150
TimeoutProvider defaultTimeoutProvider) throws Throwable {
151151

@@ -154,7 +154,7 @@ private <T> T interceptTestableMethod(Invocation<T> invocation,
154154
return intercept(invocation, invocationContext, extensionContext, timeout, defaultTimeoutProvider);
155155
}
156156

157-
private <T> T intercept(Invocation<T> invocation, ReflectiveInvocationContext<Method> invocationContext,
157+
private <T> @Nullable T intercept(Invocation<T> invocation, ReflectiveInvocationContext<Method> invocationContext,
158158
ExtensionContext extensionContext, @Nullable TimeoutDuration explicitTimeout,
159159
TimeoutProvider defaultTimeoutProvider) throws Throwable {
160160

0 commit comments

Comments
 (0)