diff --git a/.github/workflows/ci-java.yml b/.github/workflows/ci-java.yml index 1ebd054c05089..9332604951bfb 100644 --- a/.github/workflows/ci-java.yml +++ b/.github/workflows/ci-java.yml @@ -72,3 +72,11 @@ jobs: java-version: 17 run: | bazel test --flaky_test_attempts 3 //java/test/org/openqa/selenium/chrome:ChromeDriverFunctionalTest-remote + + nullaway-checks: + name: NullAway Checks + uses: ./.github/workflows/bazel.yml + with: + name: NullAway Checks + java-version: 17 + run: bazel build //java/... --//java:nullaway_level=ERROR diff --git a/MODULE.bazel b/MODULE.bazel index 423fa5b448ebf..552cba4e05639 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -179,6 +179,7 @@ maven.install( "com.google.auto.service:auto-service-annotations:1.1.1", "com.google.googlejavaformat:google-java-format:jar:1.25.0", "com.graphql-java:graphql-java:22.3", + "com.uber.nullaway:nullaway:0.12.1", "dev.failsafe:failsafe:3.3.2", "io.grpc:grpc-context:1.68.1", "io.lettuce:lettuce-core:6.5.1.RELEASE", @@ -206,6 +207,7 @@ maven.install( "org.apache.logging.log4j:log4j-core:2.24.3", "org.assertj:assertj-core:3.26.3", "org.bouncycastle:bcpkix-jdk18on:1.79", + "org.checkerframework:checker-qual:3.48.0", # Guava and NullAway version conflict "org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5", "org.hsqldb:hsqldb:2.7.4", "org.jspecify:jspecify:1.0.0", diff --git a/java/BUILD.bazel b/java/BUILD.bazel index 8634b26a80295..994f938776efe 100644 --- a/java/BUILD.bazel +++ b/java/BUILD.bazel @@ -1,3 +1,4 @@ +load("@bazel_skylib//rules:common_settings.bzl", "string_flag") load("@contrib_rules_jvm//java:defs.bzl", "spotbugs_config") load(":defs.bzl", "artifact") @@ -21,6 +22,16 @@ java_plugin( ], ) +java_plugin( + name = "nullaway", + visibility = [ + "//java:__subpackages__", + ], + deps = [ + artifact("com.uber.nullaway:nullaway"), + ], +) + java_library( name = "auto-service", exported_plugins = [ @@ -43,3 +54,27 @@ spotbugs_config( "//visibility:public", ], ) + +string_flag( + name = "nullaway_level", + build_setting_default = "NONE", + values = [ + "NONE", + "WARN", + "ERROR", + ], +) + +config_setting( + name = "use_nullaway_level_warn", + flag_values = { + ":nullaway_level": "WARN", + }, +) + +config_setting( + name = "use_nullaway_level_error", + flag_values = { + ":nullaway_level": "ERROR", + }, +) diff --git a/java/maven_install.json b/java/maven_install.json index 25b1acef66bdc..43ea5a7007e90 100644 --- a/java/maven_install.json +++ b/java/maven_install.json @@ -1,7 +1,7 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": 818842380, - "__RESOLVED_ARTIFACTS_HASH": 1188602649, + "__INPUT_ARTIFACTS_HASH": 186015662, + "__RESOLVED_ARTIFACTS_HASH": 1365610560, "artifacts": { "com.beust:jcommander": { "shasums": { @@ -177,6 +177,13 @@ }, "version": "3.3.0" }, + "com.uber.nullaway:nullaway": { + "shasums": { + "jar": "820c065050e9f1164ea7cb6704efd2c1566a71900ac0f6965a68d72c63d2068b", + "sources": "11d718a9e174151dbd041b949855af568a65675c6c0ce24a8c74258eb7898c3f" + }, + "version": "0.12.1" + }, "commons-codec:commons-codec": { "shasums": { "jar": "b3e9f6d63a790109bf0d056611fbed1cf69055826defeb9894a71369d246ed63", @@ -578,10 +585,17 @@ }, "org.checkerframework:checker-qual": { "shasums": { - "jar": "7e8554c902b9b839e61396cfe3a64c84ecabfb8eb652c410bfd8e4f5c1a8ece8", - "sources": "4f85e422b5da3c73990e894f1ed4cac45358f34fb4b979687848301584eccb57" + "jar": "c2b4dcb51ab3ea99bac3d1034d9dca9a8208d85ae3256527a85718340a00eb32", + "sources": "add740bcda7d5860913f10e0b9f638d133235b494e986fd928e1a410705ff04d" }, - "version": "3.21.2" + "version": "3.48.0" + }, + "org.checkerframework:dataflow-nullaway": { + "shasums": { + "jar": "8710f503cdc769543ee0fe0fd44c7913d89de62c6238807173346512de7f2e87", + "sources": "b7b5a38863c9c8f4d359ae5ddbcf73fa97fbf4e8c8255a94320d4d2e7102aa7e" + }, + "version": "3.48.0" }, "org.dom4j:dom4j": { "shasums": { @@ -860,6 +874,11 @@ "com.graphql-java:java-dataloader": [ "org.slf4j:slf4j-api" ], + "com.uber.nullaway:nullaway": [ + "com.google.guava:guava", + "org.checkerframework:dataflow-nullaway", + "org.jspecify:jspecify" + ], "io.grpc:grpc-context": [ "io.grpc:grpc-api" ], @@ -1031,6 +1050,9 @@ "org.bouncycastle:bcutil-jdk18on": [ "org.bouncycastle:bcprov-jdk18on" ], + "org.checkerframework:dataflow-nullaway": [ + "org.checkerframework:checker-qual" + ], "org.eclipse.mylyn.github:org.eclipse.egit.github.core": [ "com.google.code.gson:gson" ], @@ -1428,6 +1450,23 @@ "org.dataloader.stats", "org.dataloader.stats.context" ], + "com.uber.nullaway:nullaway": [ + "com.uber.nullaway", + "com.uber.nullaway.annotations", + "com.uber.nullaway.dataflow", + "com.uber.nullaway.dataflow.cfg", + "com.uber.nullaway.fixserialization", + "com.uber.nullaway.fixserialization.adapters", + "com.uber.nullaway.fixserialization.location", + "com.uber.nullaway.fixserialization.out", + "com.uber.nullaway.generics", + "com.uber.nullaway.handlers", + "com.uber.nullaway.handlers.contract", + "com.uber.nullaway.handlers.contract.fieldcontract", + "com.uber.nullaway.handlers.stream", + "com.uber.nullaway.handlers.temporary", + "com.uber.nullaway.jarinfer" + ], "commons-codec:commons-codec": [ "org.apache.commons.codec", "org.apache.commons.codec.binary", @@ -2447,12 +2486,14 @@ "org.checkerframework.checker.interning.qual", "org.checkerframework.checker.lock.qual", "org.checkerframework.checker.mustcall.qual", + "org.checkerframework.checker.nonempty.qual", "org.checkerframework.checker.nullness.qual", "org.checkerframework.checker.optional.qual", "org.checkerframework.checker.propkey.qual", "org.checkerframework.checker.regex.qual", "org.checkerframework.checker.signature.qual", "org.checkerframework.checker.signedness.qual", + "org.checkerframework.checker.sqlquotes.qual", "org.checkerframework.checker.tainting.qual", "org.checkerframework.checker.units.qual", "org.checkerframework.common.aliasing.qual", @@ -2460,11 +2501,55 @@ "org.checkerframework.common.reflection.qual", "org.checkerframework.common.returnsreceiver.qual", "org.checkerframework.common.subtyping.qual", - "org.checkerframework.common.util.report.qual", + "org.checkerframework.common.util.count.report.qual", "org.checkerframework.common.value.qual", "org.checkerframework.dataflow.qual", "org.checkerframework.framework.qual" ], + "org.checkerframework:dataflow-nullaway": [ + "javax.annotation", + "javax.annotation.concurrent", + "javax.annotation.meta", + "org.checkerframework.nullaway.com.google.common.annotations", + "org.checkerframework.nullaway.com.google.common.base", + "org.checkerframework.nullaway.com.google.common.base.internal", + "org.checkerframework.nullaway.com.google.common.cache", + "org.checkerframework.nullaway.com.google.common.collect", + "org.checkerframework.nullaway.com.google.common.escape", + "org.checkerframework.nullaway.com.google.common.eventbus", + "org.checkerframework.nullaway.com.google.common.graph", + "org.checkerframework.nullaway.com.google.common.hash", + "org.checkerframework.nullaway.com.google.common.html", + "org.checkerframework.nullaway.com.google.common.io", + "org.checkerframework.nullaway.com.google.common.math", + "org.checkerframework.nullaway.com.google.common.net", + "org.checkerframework.nullaway.com.google.common.primitives", + "org.checkerframework.nullaway.com.google.common.reflect", + "org.checkerframework.nullaway.com.google.common.util.concurrent", + "org.checkerframework.nullaway.com.google.common.util.concurrent.internal", + "org.checkerframework.nullaway.com.google.common.xml", + "org.checkerframework.nullaway.com.google.errorprone.annotations", + "org.checkerframework.nullaway.com.google.errorprone.annotations.concurrent", + "org.checkerframework.nullaway.com.google.j2objc.annotations", + "org.checkerframework.nullaway.com.google.thirdparty.publicsuffix", + "org.checkerframework.nullaway.dataflow.analysis", + "org.checkerframework.nullaway.dataflow.busyexpr", + "org.checkerframework.nullaway.dataflow.cfg", + "org.checkerframework.nullaway.dataflow.cfg.block", + "org.checkerframework.nullaway.dataflow.cfg.builder", + "org.checkerframework.nullaway.dataflow.cfg.node", + "org.checkerframework.nullaway.dataflow.cfg.playground", + "org.checkerframework.nullaway.dataflow.cfg.visualize", + "org.checkerframework.nullaway.dataflow.constantpropagation", + "org.checkerframework.nullaway.dataflow.expression", + "org.checkerframework.nullaway.dataflow.livevariable", + "org.checkerframework.nullaway.dataflow.reachingdef", + "org.checkerframework.nullaway.dataflow.util", + "org.checkerframework.nullaway.javacutil", + "org.checkerframework.nullaway.javacutil.trees", + "org.checkerframework.nullaway.org.plumelib.reflection", + "org.checkerframework.nullaway.org.plumelib.util" + ], "org.dom4j:dom4j": [ "org.dom4j", "org.dom4j.bean", @@ -2933,6 +3018,8 @@ "com.graphql-java:graphql-java:jar:sources", "com.graphql-java:java-dataloader", "com.graphql-java:java-dataloader:jar:sources", + "com.uber.nullaway:nullaway", + "com.uber.nullaway:nullaway:jar:sources", "commons-codec:commons-codec", "commons-codec:commons-codec:jar:sources", "commons-io:commons-io", @@ -3049,6 +3136,8 @@ "org.bouncycastle:bcutil-jdk18on:jar:sources", "org.checkerframework:checker-qual", "org.checkerframework:checker-qual:jar:sources", + "org.checkerframework:dataflow-nullaway", + "org.checkerframework:dataflow-nullaway:jar:sources", "org.dom4j:dom4j", "org.dom4j:dom4j:jar:sources", "org.eclipse.mylyn.github:org.eclipse.egit.github.core", diff --git a/java/private/export.bzl b/java/private/export.bzl index 41a41801a7385..33e7b640b4969 100644 --- a/java/private/export.bzl +++ b/java/private/export.bzl @@ -1,4 +1,3 @@ -load("@contrib_rules_jvm//java:defs.bzl", "java_library") load( "@rules_jvm_external//:defs.bzl", "javadoc", @@ -8,6 +7,7 @@ load("@rules_jvm_external//private/rules:maven_bom_fragment.bzl", "maven_bom_fra load("@rules_jvm_external//private/rules:maven_project_jar.bzl", "maven_project_jar") load("@rules_jvm_external//private/rules:maven_publish.bzl", "maven_publish") load("//java/private:module.bzl", "java_module") +load(":java_library.bzl", "java_library") def java_export( name, diff --git a/java/private/java_library.bzl b/java/private/java_library.bzl new file mode 100644 index 0000000000000..0273b48f0d185 --- /dev/null +++ b/java/private/java_library.bzl @@ -0,0 +1,51 @@ +load( + "@contrib_rules_jvm//java:defs.bzl", + _java_library = "java_library", +) + +def java_library( + name, + deps = [], + srcs = [], + exports = [], + tags = [], + visibility = None, + javacopts = [], + plugins = [], + **kwargs): + # NullAway configuration + nullaway_plugins = select({ + "//java:use_nullaway_level_warn": [ + "//java:nullaway", + ], + "//java:use_nullaway_level_error": [ + "//java:nullaway", + ], + "//conditions:default": [], + }) + nullaway_javacopts = select({ + "//java:use_nullaway_level_warn": [ + "-Xep:NullAway:WARN", + "-XepOpt:NullAway:AnnotatedPackages=org.openqa.selenium", + "-XepOpt:NullAway:JSpecifyMode=true", + ], + "//java:use_nullaway_level_error": [ + "-Xep:NullAway:ERROR", + "-XepOpt:NullAway:AnnotatedPackages=org.openqa.selenium", + "-XepOpt:NullAway:JSpecifyMode=true", + ], + "//conditions:default": [], + }) + + # global place for NullAway plugin use + _java_library( + name = name, + deps = deps, + srcs = srcs, + exports = exports, + tags = tags, + visibility = visibility, + plugins = plugins + nullaway_plugins, + javacopts = javacopts + nullaway_javacopts, + **kwargs + ) diff --git a/java/private/library.bzl b/java/private/library.bzl index f2f7d7f69a2d8..a83b738e152b1 100644 --- a/java/private/library.bzl +++ b/java/private/library.bzl @@ -1,9 +1,9 @@ load( "@contrib_rules_jvm//java:defs.bzl", - _java_library = "java_library", _java_test = "java_test", ) load(":export.bzl", _java_export = "java_export") +load(":java_library.bzl", _java_library = "java_library") java_export = _java_export java_library = _java_library diff --git a/java/src/org/openqa/selenium/grid/jmx/JMXHelper.java b/java/src/org/openqa/selenium/grid/jmx/JMXHelper.java index ea527ed6c5254..2b4f19f6adebe 100644 --- a/java/src/org/openqa/selenium/grid/jmx/JMXHelper.java +++ b/java/src/org/openqa/selenium/grid/jmx/JMXHelper.java @@ -27,6 +27,7 @@ public class JMXHelper { private static final Logger LOG = Logger.getLogger(JMXHelper.class.getName()); + @SuppressWarnings("NullAway") public MBean register(Object bean) { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); MBean mBean = new MBean(bean); diff --git a/java/src/org/openqa/selenium/grid/jmx/MBean.java b/java/src/org/openqa/selenium/grid/jmx/MBean.java index 501cd254d27d5..9687815ed9a93 100644 --- a/java/src/org/openqa/selenium/grid/jmx/MBean.java +++ b/java/src/org/openqa/selenium/grid/jmx/MBean.java @@ -57,6 +57,7 @@ private static class AttributeInfo { this.setter = setter; } + @SuppressWarnings("NullAway") MBeanAttributeInfo getMBeanAttributeInfo() { try { return new MBeanAttributeInfo(name, description, getter, setter); @@ -116,6 +117,7 @@ private void collectAttributeInfo(Object bean) { .forEach(ai -> attributeMap.put(ai.name, ai)); } + @SuppressWarnings("NullAway") private AttributeInfo getAttributeInfo(Method m) { ManagedAttribute ma = m.getAnnotation(ManagedAttribute.class); if (ma == null) { @@ -130,6 +132,7 @@ private AttributeInfo getAttributeInfo(Method m) { } } + @SuppressWarnings("NullAway") private Method findGetter(Method annotatedMethod) { ManagedAttribute ma = annotatedMethod.getAnnotation(ManagedAttribute.class); try { @@ -151,6 +154,7 @@ private Method findGetter(Method annotatedMethod) { } } + @SuppressWarnings("NullAway") private Method findSetter(Method annotatedMethod) { ManagedAttribute ma = annotatedMethod.getAnnotation(ManagedAttribute.class); if (!"".equals(ma.setter())) { @@ -170,6 +174,7 @@ private Method findSetter(Method annotatedMethod) { return null; } + @SuppressWarnings("NullAway") private Method findMethod(Class cls, String name) { return Stream.of(cls.getMethods()) .filter(m -> m.getName().equals(name)) @@ -184,6 +189,7 @@ private void collectOperationInfo(Object bean) { .forEach(oi -> operationMap.put(oi.name, oi)); } + @SuppressWarnings("NullAway") private OperationInfo getOperationInfo(Method m) { ManagedOperation mo = m.getAnnotation(ManagedOperation.class); if (mo == null) { @@ -213,6 +219,7 @@ private ObjectName generateObjectName(Object bean) { } } + @SuppressWarnings("NullAway") @Override public Object getAttribute(String attribute) { try { @@ -232,6 +239,7 @@ public Object getAttribute(String attribute) { } } + @SuppressWarnings("NullAway") @Override public void setAttribute(Attribute attribute) { try { @@ -256,11 +264,13 @@ public AttributeList getAttributes(String[] attributes) { return resultList; } + @SuppressWarnings("NullAway") @Override public AttributeList setAttributes(AttributeList attributes) { return null; } + @SuppressWarnings("NullAway") @Override public Object invoke(String actionName, Object[] params, String[] signature) { try {