Skip to content

Commit 5d61833

Browse files
authored
Merge pull request #196 from graphql-java/errorprone-plus-nullaway-support
Error Prone / NullAway support for JSpecify
2 parents cabda40 + afd5dc1 commit 5d61833

File tree

6 files changed

+99
-13
lines changed

6 files changed

+99
-13
lines changed

build.gradle

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2+
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
3+
import net.ltgt.gradle.errorprone.CheckSeverity
14
import java.text.SimpleDateFormat
25

36
plugins {
@@ -11,11 +14,28 @@ plugins {
1114
id 'io.github.gradle-nexus.publish-plugin' version '1.0.0'
1215
id 'com.github.ben-manes.versions' version '0.51.0'
1316
id "me.champeau.jmh" version "0.7.3"
17+
id "net.ltgt.errorprone" version '4.2.0'
18+
19+
// Kotlin just for tests - not production code
20+
id 'org.jetbrains.kotlin.jvm' version '2.1.21'
1421
}
1522

1623
java {
1724
toolchain {
18-
languageVersion = JavaLanguageVersion.of(11)
25+
languageVersion = JavaLanguageVersion.of(17)
26+
}
27+
}
28+
29+
kotlin {
30+
compilerOptions {
31+
apiVersion = KotlinVersion.KOTLIN_2_0
32+
languageVersion = KotlinVersion.KOTLIN_2_0
33+
jvmTarget = JvmTarget.JVM_11
34+
javaParameters = true
35+
freeCompilerArgs = [
36+
'-Xemit-jvm-type-annotations',
37+
'-Xjspecify-annotations=strict',
38+
]
1939
}
2040
}
2141

@@ -75,8 +95,35 @@ dependencies {
7595
// this is needed for the idea jmh plugin to work correctly
7696
jmh 'org.openjdk.jmh:jmh-core:1.37'
7797
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.37'
98+
99+
errorprone 'com.uber.nullaway:nullaway:0.12.6'
100+
errorprone 'com.google.errorprone:error_prone_core:2.37.0'
101+
102+
// just tests
103+
testCompileOnly 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
78104
}
79105

106+
tasks.withType(JavaCompile) {
107+
options.release = 11
108+
options.errorprone {
109+
disableAllChecks = true
110+
check("NullAway", CheckSeverity.ERROR)
111+
//
112+
// end state has us with this config turned on - eg all classes
113+
//
114+
//option("NullAway:AnnotatedPackages", "org.dataloader")
115+
option("NullAway:OnlyNullMarked", "true")
116+
option("NullAway:JSpecifyMode", "true")
117+
}
118+
// Include to disable NullAway on test code
119+
if (name.toLowerCase().contains("test")) {
120+
options.errorprone {
121+
disable("NullAway")
122+
}
123+
}
124+
}
125+
126+
80127
task sourcesJar(type: Jar) {
81128
dependsOn classes
82129
archiveClassifier.set('sources')

src/main/java/org/dataloader/BatchLoaderEnvironment.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
@NullMarked
2020
public class BatchLoaderEnvironment {
2121

22-
private final Object context;
22+
private final @Nullable Object context;
2323
private final Map<Object, Object> keyContexts;
2424
private final List<Object> keyContextsList;
2525

26-
private BatchLoaderEnvironment(Object context, List<Object> keyContextsList, Map<Object, Object> keyContexts) {
26+
private BatchLoaderEnvironment(@Nullable Object context, List<Object> keyContextsList, Map<Object, Object> keyContexts) {
2727
this.context = context;
2828
this.keyContexts = keyContexts;
2929
this.keyContextsList = keyContextsList;
@@ -33,7 +33,6 @@ private BatchLoaderEnvironment(Object context, List<Object> keyContextsList, Map
3333
* Returns the overall context object provided by {@link org.dataloader.BatchLoaderContextProvider}
3434
*
3535
* @param <T> the type you would like the object to be
36-
*
3736
* @return a context object or null if there isn't one
3837
*/
3938
@SuppressWarnings("unchecked")
@@ -68,7 +67,7 @@ public static Builder newBatchLoaderEnvironment() {
6867
}
6968

7069
public static class Builder {
71-
private Object context;
70+
private @Nullable Object context;
7271
private Map<Object, Object> keyContexts = Collections.emptyMap();
7372
private List<Object> keyContextsList = Collections.emptyList();
7473

src/main/java/org/dataloader/DataLoader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
*/
6969
@PublicApi
7070
@NullMarked
71-
public class DataLoader<K, V> {
71+
public class DataLoader<K, V extends @Nullable Object> {
7272

7373
private final @Nullable String name;
7474
private final DataLoaderHelper<K, V> helper;

src/main/java/org/dataloader/DataLoaderRegistry.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.dataloader;
22

33
import org.dataloader.annotations.PublicApi;
4+
import org.dataloader.impl.Assertions;
45
import org.dataloader.instrumentation.ChainedDataLoaderInstrumentation;
56
import org.dataloader.instrumentation.DataLoaderInstrumentation;
67
import org.dataloader.instrumentation.DataLoaderInstrumentationHelper;
@@ -14,6 +15,7 @@
1415
import java.util.LinkedHashMap;
1516
import java.util.List;
1617
import java.util.Map;
18+
import java.util.Objects;
1719
import java.util.Set;
1820
import java.util.concurrent.ConcurrentHashMap;
1921
import java.util.function.Function;
@@ -141,8 +143,7 @@ private static DataLoaderOptions setInInstrumentation(DataLoaderOptions options,
141143
* @return this registry
142144
*/
143145
public DataLoaderRegistry register(DataLoader<?, ?> dataLoader) {
144-
String name = dataLoader.getName();
145-
assertState(name != null, () -> "The DataLoader must have a non null name");
146+
String name = Assertions.nonNull(dataLoader.getName(), () -> "The DataLoader must have a non null name");
146147
dataLoaders.put(name, nameAndInstrumentDL(name, instrumentation, dataLoader));
147148
return this;
148149
}
@@ -176,7 +177,7 @@ public DataLoaderRegistry register(String key, DataLoader<?, ?> dataLoader) {
176177
*/
177178
public <K, V> DataLoader<K, V> registerAndGet(String key, DataLoader<?, ?> dataLoader) {
178179
dataLoaders.put(key, nameAndInstrumentDL(key, instrumentation, dataLoader));
179-
return getDataLoader(key);
180+
return Objects.requireNonNull(getDataLoader(key));
180181
}
181182

182183
/**
@@ -251,10 +252,10 @@ public DataLoaderRegistry unregister(String key) {
251252
* @param key the key of the data loader
252253
* @param <K> the type of keys
253254
* @param <V> the type of values
254-
* @return a data loader or null if its not present
255+
* @return a data loader or null if it's not present
255256
*/
256257
@SuppressWarnings("unchecked")
257-
public <K, V> DataLoader<K, V> getDataLoader(String key) {
258+
public <K, V> @Nullable DataLoader<K, V> getDataLoader(String key) {
258259
return (DataLoader<K, V>) dataLoaders.get(key);
259260
}
260261

src/test/java/org/dataloader/DataLoaderRegistryTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.dataloader;
22

3-
import org.dataloader.impl.DataLoaderAssertionException;
43
import org.dataloader.stats.SimpleStatisticsCollector;
54
import org.dataloader.stats.Statistics;
65
import org.junit.jupiter.api.Assertions;
@@ -63,7 +62,7 @@ public void registration_works() {
6362
try {
6463
registry.register(dlUnnamed);
6564
Assertions.fail("Should have thrown an exception");
66-
} catch (DataLoaderAssertionException ignored) {
65+
} catch (NullPointerException ignored) {
6766
}
6867
}
6968

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.dataloader
2+
3+
import org.junit.jupiter.api.Test
4+
import java.util.concurrent.CompletableFuture
5+
import java.util.concurrent.CompletableFuture.*
6+
7+
/**
8+
* Some Kotlin code to prove that are JSpecify annotations work here
9+
* as expected in Kotlin land. We don't intend to ue Kotlin in our tests
10+
* or to deliver Kotlin code in the java
11+
*/
12+
class KotlinExamples {
13+
14+
@Test
15+
fun `basic kotlin test of non nullable value types`() {
16+
val dataLoader: DataLoader<String, String> = DataLoaderFactory.newDataLoader { keys -> completedFuture(keys.toList()) }
17+
18+
val cfA = dataLoader.load("A")
19+
val cfB = dataLoader.load("B")
20+
21+
dataLoader.dispatch()
22+
23+
assert(cfA.join().equals("A"))
24+
assert(cfA.join().equals("A"))
25+
}
26+
27+
@Test
28+
fun `basic kotlin test of nullable value types`() {
29+
val dataLoader: DataLoader<String, String?> = DataLoaderFactory.newDataLoader { keys -> completedFuture(keys.toList()) }
30+
31+
val cfA = dataLoader.load("A")
32+
val cfB = dataLoader.load("B")
33+
34+
dataLoader.dispatch()
35+
36+
assert(cfA.join().equals("A"))
37+
assert(cfA.join().equals("A"))
38+
}
39+
40+
}

0 commit comments

Comments
 (0)