NullAwayAnnotator
, or simply Annotator
, is a tool that automatically infers nullability types in the given source code and injects the corresponding annotations to pass NullAway checks.
Applying NullAway to build systems requires manual effort in annotating the source code. Even if the code is free of nullability errors, annotations are still needed to pass NullAway checks. A tool that can automatically infer types in the source code and inject the corresponding annotations to pass NullAway checks can significantly reduce the effort of integrating NullAway into build systems.
Annotator
minimizes the number of reported NullAway errors by inferring nullability types of elements in the source code and injecting the corresponding annotations. For errors that are not resolvable with any annotations, Annotator injects appropriate suppression annotations. The final output of Annotator is a source code that passes NullAway checks with no remaining errors.
In the code below, NullAway
reports five warnings.
class Test{
Object f1 = null; // warning: assigning @Nullable expression to @NonNull field
Object f2 = null; // warning: assigning @Nullable expression to @NonNull field
Object f3 = null; // warning: assigning @Nullable expression to @NonNull field
Object f4 = null; // warning: assigning @Nullable expression to @NonNull field
Object f5 = f4;
Object f6 = new Object();
String m1(){
return f1 != null ? f1.toString() : f2.toString() + f6.toString();
}
int m2(){
return f3 != null ? f3.hashCode() : f2.hashCode() + f6.hashCode();
}
Object m3(){
return f5;
}
void m4(){
f6 = null; // warning: assigning @Nullable expression to @NonNull field
}
}
Annotator
can infer the nullable types in the code above and inject the corresponding annotations. For unresolved errors, suppression annotations are injected.
The output below shows the result of running Annotator
on the code above.
import javax.annotation.Nullable; // added by Annotator
import org.jspecify.annotations.NullUnmarked; // added by Annotator
class Test{
@Nullable Object f1 = null;
@SuppressWarnings("NullAway") Object f2 = null; // inferred to be @Nonnull, and null assignment is suppressed.
@Nullable Object f3 = null;
@Nullable Object f4 = null;
@Nullable Object f5 = f4;
Object f6 = new Object(); // inferred to be @Nonnull
String m1(){
return f1 != null ? f1.toString() : f2.toString() + f6.toString();
}
int m2(){
return f3 != null ? f3.hashCode() : f2.hashCode() + f6.hashCode();
}
@Nullable Object m3(){ // inferred to be @Nullable as a result of f5 being @Nullable.
return f5;
}
@NullUnmarked //f6 is inferred to be @Nonnull, but it is assigned to null. The error is suppressed by @NullUnmarked.
void m4(){
f6 = null;
}
}
Annotator
propagates the effects of a change throughout the entire module and injects several follow-up annotations to fully resolve a specific warning.
It is also capable of processing modules within monorepos, taking into account the modules public APIs and the impacts of annotations on downstream dependencies for improved results.
We ship Annotator on Maven, as a JAR. You can find the artifact information below -
GROUP: edu.ucr.cs.riple.annotator
ID: annotator-core
ID: annotator-scanner
This sections describes how to run Annotator
on any project.
NullAway
checker must be activated with version >= 0.10.10
AnnotatorScanner
checker must be activated with version >= 1.3.6
, see more about AnnotatorScanner
here.Since Nullaway is built as a plugin for Error Prone, we need to set the following flags in our build.gradle,
"-Xep:NullAway:ERROR", // to activate NullAway
"-XepOpt:NullAway:SerializeFixMetadata=true",
"-XepOpt:NullAway:FixSerializationConfigPath=path_to_nullaway_config.xml",
"-Xep:AnnotatorScanner:ERROR", // to activate Annotator AnnotatorScanner
"-XepOpt:AnnotatorScanner:ConfigPath=path_to_scanner_config.xml",
The following code snippet demonstrates how to configure the JavaCompile
tasks in your build.gradle
to use NullAway as a plugin for Error Prone:
dependencies {
annotationProcessor 'edu.ucr.cs.riple.annotator:annotator-scanner:1.3.6'
annotationProcessor "com.uber.nullaway:nullaway:0.10.10"
errorprone "com.google.errorprone:error_prone_core:2.4.0"
errorproneJavac "com.google.errorprone:javac:9+181-r4173-1"
//All other target project dependencies
}
tasks.withType(JavaCompile) {
// remove the if condition if you want to run NullAway on test code
if (!name.toLowerCase().contains("test")) {
options.errorprone {
check("NullAway", CheckSeverity.ERROR)
check("AnnotatorScanner", CheckSeverity.ERROR)
option("NullAway:AnnotatedPackages", "org.example")
option("NullAway:SerializeFixMetadata", "true")
option("NullAway:FixSerializationConfigPath", "path_to/nullaway.xml")
option("AnnotatorScanner:ConfigPath", "path_to/scanner.xml")
}
options.compilerArgs << "-Xmaxerrs"<< "100000"
options.compilerArgs << "-Xmaxwarns" << "100000"
}
}
path_to_nullaway_config.xml
and path_to_scanner_config.xml
are configuration files that do not need to be created during the initial project setup. The script will generate these files, facilitating seamless communication between the script and the analysis. At this point, the target project is prepared for the Annotator to process.
You must provide the Annotator with the paths to path_to_nullaway_config.xml
and path_to_scanner_config.xml
. Further details on this process are described in the sections below.
Annotator
necessitates specific flag values for successful execution. You can provide these values through command line arguments.
To run Annotator
on the target project P
, the arguments below must be passed to Annotator
:
Flag | Description |
---|---|
-bc,--build-command <arg> |
Command to run NullAway on target P enclosed in "". Please note that this command should be executable from any directory (e.g., "cd /Absolute/Path/To/P && ./build" ). |
-i,--initializer <arg> |
Fully qualified name of the @Initializer annotation. |
-d,--dir <arg> |
Directory where all outputs of AnnotatorScanner and NullAway are serialized. |
-cp, --config-paths |
Path to a TSV file containing values defined in Error Prone config paths given in the format: (path_to_nullaway_config.xml \t path_to_scanner_config ). |
-cn, --checker-name |
Checker name to be used for the analysis. (use NULLAWAY to request inference for NullAway.) |
By default, Annotator
has the configuration below:
5
.javax.annotation.Nullable
as @Nullable
annotation.Here are some useful optional arguments that can alter the default configurations mentioned above:
Flag | Description |
---|---|
-n,--nullable <arg> |
Sets custom @Nullable annotation. |
-rboserr, --redirect-build-output-stderr |
Redirects build outputs to STD Err . |
To learn more about all the optional arguments, please refer to OPTIONS.md
Here is a template command you can use to run Annotator from the CLI, using CLI options-
java -jar ./path/to/annotator-core.jar -d "/path/to/output/directory" -cp "/path/to/config/paths.tsv" -i com.example.Initializer -bc "cd /path/to/targetProject && ./gradlew build -x test"
To view descriptions of all flags, simply run the JAR with the --help
option.
Annotator
version 1.3.6
is compatible with NullAway
version 0.10.10
and above.Annotator
is released under the MIT License. See the LICENSE file for more information.