This chapter discusses how to run a checker from the command line, from a build system, or from an IDE. You can skip to the appropriate section:
• Android (Section 39.1)
• Android Studio and the Android Gradle Plugin (Section 39.2)
• Ant (Section 39.3)
• Bazel (Section 39.4)
• Buck (Section 39.5)
• Command line, via Checker Framework javac wrapper (Section 39.6)
• Command line, via JDK javac (Section 39.7)
• Eclipse (Section 39.8)
• Gradle (Section 39.9)
• IntelliJ IDEA (Section 39.10)
• javac diagnostics wrapper (Section 39.11)
• Lombok (Section 39.12)
• Maven (Section 39.13)
• NetBeans (Section 39.14)
• sbt (Section 39.15)
• tIDE (Section 39.16)
If your build system or IDE is not listed above, you should customize how it runs the javac command on your behalf. See your build system or IDE documentation to learn how to customize it, adapting the instructions for javac in Section 39.7. If you make another tool support running a checker, please inform us via the mailing list or issue tracker so we can add it to this manual.
All examples in this chapter are in the public domain, with no copyright or licensing restrictions.
When creating an Android app, you may wish to use checker-qual-android whenever this document mentions checker-qual. This can lead to smaller dex files (smaller distributed apps).
The checker-qual-android artifact is identical to the checker-qual artifact, except that in checker-qual-android annotations have classfile retention. The default Android Gradle plugin retains types annotated with runtime-retention annotations in the main dex, but
strips out class-retention annotations.
Android Studio 3.0 and later, and Android Gradle Plugin 3.0.0 and later, support type annotations. (See https://developer.android.com/studio/write/java8-support for more details.) This section explains
how to configure your Android project to use the Checker Framework. All the changes should be made to the module’s build.gradle file — not the project’s build.gradle file.
1. Add a build variant for running checkers:
android {
...
buildTypes {
...
checkTypes {
javaCompileOptions.annotationProcessorOptions.
classNames.add("org.checkerframework.checker.nullness.NullnessChecker")
// You can pass options like so:
// javaCompileOptions.annotationProcessorOptions.arguments.put("warns", "")
}
}
}
2. Declare the Checker Framework dependencies:
dependencies {
... existing dependencies...
ext.checkerFrameworkVersion = "4.3.0"
implementation "org.checkerframework:checker-qual-android:${checkerFrameworkVersion}"
// or if you use no annotations in source code the above line could be
// compileOnly "org.checkerframework:checker-qual-android:${checkerFrameworkVersion}"
annotationProcessor "org.checkerframework:checker:${checkerFrameworkVersion}"
}
3. To run the checkers, build using the checkTypes variant:
gradlew assembleCheckTypes
If you use the Ant build tool to compile your software, then you can add an Ant task that runs a checker. We assume that your Ant file already contains a compilation target that uses the javac task, and that the
CHECKERFRAMEWORK environment variable is set.
1. Set the cfJavac property:
<property environment="env"/>
<property name="checkerframework" value="${env.CHECKERFRAMEWORK}" />
<condition property="cfJavac" value="javac.bat" else="javac">
<os family="windows" />
</condition>
<presetdef name="cf.javac">
<javac fork="yes" executable="${checkerframework}/checker/bin/${cfJavac}" >
<compilerarg value="-version"/>
<compilerarg value="-implicit:class"/>
</javac>
</presetdef>
2. Duplicate the compilation target, then modify it slightly as indicated in this example:
<target name="check-nullness"
description="Check for null pointer dereferences"
depends="clean,...">
<!-- use cf.javac instead of javac -->
<cf.javac ... >
<compilerarg line="-processor org.checkerframework.checker.nullness.NullnessChecker"/>
<!-- optional, to not check uses of library methods:
<compilerarg value="-AskipUses=^(java\.awt\.|javax\.swing\.)"/>
-->
<compilerarg line="-Xmaxerrs 10000"/>
...
</cf.javac>
</target>
Fill in each ellipsis (…) from the original compilation target. However, do not copy any fork= setting from the original <javac> task invocation.
If your original compilation target set the bootclasspath, then you cannot use the javac wrapper script as the above instructions do. You should edit your Ant buildfile to make invocations similar to those described in Section 39.7, but accommodating your bootclasspath.
In the example, the target is named check-nullness, but you can name it whatever you like.
This section explains each part of the Ant task.
1. Definition of cf.javac:
The fork field of the javac task ensures that an external javac program is called. Otherwise, Ant will run javac via a Java method call, and there is no guarantee that it will get the correct version of javac.
The -version compiler argument is just for debugging; you may omit it.
The -implicit:class compiler argument causes annotation processing to be performed on implicitly compiled files. (An implicitly compiled file is one that was not specified on the command line, but for which the source code is newer than the .class file.) This is the default, but
supplying the argument explicitly suppresses a compiler warning.
2. The check-nullness target:
The target assumes the existence of a clean target that removes all .class files. That is necessary because Ant’s javac target doesn’t re-compile .java files for which a .class file already exists.
The -processor ... compiler argument indicates which checker to run. You can supply additional arguments to the checker as well.
Bazel is a build system maintained by Google.
To run the Checker Framework, see Stack Overflow discussion 48555320 (https://stackoverflow.com/questions/48555320/).
Buck is a build system maintained by Meta.
Buck has support for annotation processors, but that support is undocumented because the Buck maintainers may change the syntax in the future and they don’t wish to ever change anything that is documented. You can learn more about Buck and annotation processors at these URLs: https://stackoverflow.com/questions/32915721/documentation-for-annotation-processors-buck, https://github.com/facebook/buck/issues/85.
Here is an example BUCK build file showing how to enable the Checker Framework:
prebuilt_jar(
name = 'checker-framework',
binary_jar = 'checker-4.3.0.jar',
visibility = [ 'PUBLIC' ]
)
prebuilt_jar(
name = 'checker-qual',
binary_jar = 'checker-qual-4.3.0.jar',
visibility = [ 'PUBLIC' ]
)
java_library (
name = 'hello',
srcs = glob(['src/main/java/**/*.java']),
java_version = '17',
provided_deps = [ ':checker-framework', ':checker-qual' ],
# To add annotation processing
annotation_processors = [ 'org.checkerframework.checker.units.UnitsChecker' ],
annotation_processor_deps = [ ':checker-framework', ':checker-qual' ],
)
Use --verbose 8 to see diagnostic output including a command line that ought to be equivalent to Buck’s use of an in-process Java compiler. For example, you might run
./buck clean ./buck fetch //... ./buck build --verbose 8 //app:main
However, the command lines that Buck prints with -v 8 are not what Buck actually executes. Buck’s invocation of the Checker Framework may produce errors that cannot be reproduced from the command line. This is because Buck uses in-process compilation, potentially multi-threaded, and because Buck may use multiple classloaders that are cached.
You can force Buck to run an external javac, thus behaving exactly like the command line that its diagnostics print. Use the configuration flag -c tools.javac=path-to-javac, for example:
./buck clean && ./buck fetch //... && ./buck build //app:main -c tools.javac=$(which javac)
To perform pluggable type-checking from the command line, run the javac command that ships with the Checker Framework. This is called the “Checker Framework compiler” or the “Checker Framework javac wrapper”. It is exactly the same as the OpenJDK javac
compiler, with one small difference: it includes the Checker Framework jar file on its classpath.
You cannot use the javac wrapper if you wish to set the bootclasspath.
There are three ways to use the Checker Framework compiler from the command line. You can use any one of them. However, if you are using the Windows command shell, you must use the last one. Adjust the pathnames if you have installed the Checker Framework somewhere other than
$HOME/checker-framework-4.3.0/.
• Option 1: Add directory .../checker-framework-4.3.0/checker/bin to your path, before any other directory that contains a javac executable.
If you are using the bash shell, a way to do this is to add the following to your ~/.profile (or alternately ~/.bash_profile or ~/.bashrc) file:
export CHECKERFRAMEWORK=${HOME}/checker-framework-4.3.0
export PATH=${CHECKERFRAMEWORK}/checker/bin:${PATH}
After editing the file, log out and back in to ensure that the environment variable setting takes effect.
•
Option 2: Whenever this document tells you to run javac, instead run $CHECKERFRAMEWORK/checker/bin/javac.
You can simplify this by introducing an alias javacheck. Then, whenever this document tells you to run javac, instead run javacheck. Here is the syntax for your ~/.bashrc, ~/.profile, or ~/.bash_profile file:
export CHECKERFRAMEWORK=${HOME}/checker-framework-4.3.0
alias javacheck='$CHECKERFRAMEWORK/checker/bin/javac'
After editing the file, log out and back in to ensure that the environment variable setting and alias take effect.
• Option 3: Whenever this document tells you to run javac, instead run checker.jar via java (not javac) as in:
java -jar "$CHECKERFRAMEWORK/checker/dist/checker.jar" -cp "myclasspath" -processor nullness MyFile.java
You can simplify the above command by introducing an alias javacheck. Then, whenever this document tells you to run javac, instead run javacheck. For example:
# Unix
export CHECKERFRAMEWORK=${HOME}/checker-framework-4.3.0
alias javacheck='java -jar "$CHECKERFRAMEWORK/checker/dist/checker.jar"'
# Windows
set CHECKERFRAMEWORK = C:\Program Files\checker-framework-4.3.0\
doskey javacheck=java -jar "%CHECKERFRAMEWORK%\checker\dist\checker.jar" $*
(Explanation for advanced users: More generally, anywhere that you would use javac.jar, you can substitute $CHECKERFRAMEWORK/checker/dist/checker.jar; the result is to use the Checker Framework compiler instead of the regular javac.)
This section explains how to use the Checker Framework with the OpenJDK or OracleJDK javac, rather than with the javac wrapper script described in Section 39.6.
This section assumes you have downloaded the Checker Framework release zip and set the environment variable CHECKERFRAMEWORK to point to the unzipped directory. Alternately, you can get all of the jars mentioned in this section from Maven Central:
• checker-qual.jar: https://repo1.maven.org/maven2/org/checkerframework/checker-qual/4.3.0/checker-qual-4.3.0.jar
• checker-util.jar: https://repo1.maven.org/maven2/org/checkerframework/checker-util/4.3.0/checker-util-4.3.0.jar
• checker.jar: https://repo1.maven.org/maven2/org/checkerframework/checker/4.3.0/checker-4.3.0-all.jar
These instructions should work on JDK 17 or later, but we only test with supported versions of Java: JDK 17, JDK 21, and JDK 25.
To compile non-modularized code:
javac \ -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ -cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ -processor org.checkerframework.checker.nullness.NullnessChecker
The arguments are explained above, except for two of them:
1. -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED which opens the jdk.compiler/com.sun.tools.javac.comp package. This is required because the Checker Framework reflectively accesses private members of this package.
2. -J--add-exports=...=ALL-UNNAMED which exports the listed packages. This is required because the Checker Framework accesses non-exported members of these packages.
If these options are missing, javac may issue one of the following warnings:
WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.checkerframework.javacutil.Resolver (file:$CHECKERFRAMEWORK/checker/dist/checker.jar) to method com.sun.tools.javac.comp.Resolve.findMethod(...) WARNING: Please consider reporting this to the maintainers of org.checkerframework.javacutil.Resolver WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release
java.lang.IllegalAccessError: class org.checkerframework.javacutil.AbstractTypeProcessor (in unnamed module ...) cannot access class com.sun.tools.javac.processing.JavacProcessingEnvironment (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.processing to unnamed module ...
To compile a module, first add requires org.checkerframework.checker.qual; to your module-info.java. The Checker Framework inserts inferred annotations into bytecode even if none appear in source code, so you must do this even if you write no annotations in your code.
javac \ -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ -processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar \ --module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar \ -processor org.checkerframework.checker.nullness.NullnessChecker
checker-qual.jar must be on the module path rather than the classpath, but do not put checker.jar on the processor module path, as it is not modularized.
You need to run the Checker Framework via a build tool (Ant, Gradle, Maven, etc.), rather than by supplying the -processor command-line option to the ejc compiler, which is also known as eclipsec. The reason is that the Checker Framework is built upon
javac, and ejc represents the Java program differently. (If both javac and ejc implemented JSR 198 [Cro06], then it would be possible to build an annotation processor that works with both compilers.)
There is no dedicated Eclipse plug-in for running the Checker Framework, but it’s still easy to run the Checker Framework. First, create a target/task in your build system to run the Checker Framework. Then, run the target/task from Eclipse. Section 39.8.1 gives details for Ant, but other build systems are similar.
Add an Ant target as described in Section 39.3. You can run the Ant target by executing the following steps (instructions copied from https://help.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.platform.doc.user%2FgettingStarted%2Fqs-84_run_ant.htm):
1. Select build.xml in one of the navigation views and choose Run As > Ant Build... from its context menu.
2. A launch configuration dialog is opened on a launch configuration for this Ant buildfile.
3. In the Targets tab, select the new Ant task (e.g., check-interning).
4. Click Run.
5. The Ant buildfile is run, and the output is sent to the Console view.
Eclipse issues an “Unhandled Token in @SuppressWarnings” warning if you write a @SuppressWarnings annotation containing a string that Eclipse does not know about. Unfortunately, Eclipse hard-codes this list.
To eliminate the warnings: disable all “Unhandled Token in @SuppressWarnings” warnings in Eclipse. Look under the menu headings “Java → Compiler → Errors/Warnings → Annotations → Unhandled Token in ’@SuppressWarnings’,” and set it to ignore.
To run a checker on a project that uses the Gradle build system, use the Checker Framework Gradle plugin. Its documentation explains how to use it.
This section tells you how to make IntelliJ IDEA automatically run a checker on every compile for Java projects and/or modules.
If your project uses a build tool (Ant, Gradle, Maven, etc.), do not use the instructions in this section. Follow the instructions for that build tool instead (they are in a different section of this chapter). To compile your project, run the build tool (possibly from within IntelliJ IDEA, possibly not).
If your project does not use a build tool, use the following instructions to run a checker within IntelliJ IDEA on every compile:
1. Change the project SDK to 17 or later, as explained at https://www.jetbrains.com/help/idea/sdk.html#change-project-sdk.
2. Make sure the language level for your project is 8 or higher, as explained at https://www.jetbrains.com/help/idea/project-page.html.
3. Create and configure an annotation profile, following the instructions at https://www.jetbrains.com/help/idea/annotation-processors-support.html. When configuring the profile:
(a) Add .../checker-framework/checker/dist/checker.jar to the processor path.
(b) Add checkers to be run during compilation by writing the fully-qualified name of the checker in the “Processor FQ Name” section. An example of a fully-qualified checker name is “org.checkerframework.checker.nullness.NullnessChecker”.
4. Add .../checker-framework/checker/dist/checker-qual.jar as a dependency to all modules you wish to type-check. (They should all have been associated with the annotation profile above.) Instructions appear at https://www.jetbrains.com/help/idea/creating-and-managing-projects.html.
Now, when you compile your code, the checker will be run.
It is necessary to manually inform the IDE via a plugin if an annotation system adds any dependencies beyond those that normally exist in Java. For information about the extension points, see https://youtrack.jetbrains.com/issue/IDEA-159286.
To make IntelliJ compile on every change or save, follow the instructions at https://www.jetbrains.com/help/idea/compiling-applications.html#auto-build.
You can also configure IntelliJ to automatically save (and thus automatically compile) your work periodically. Instructions appear at https://www.jetbrains.com/help/idea/system-settings.html#sync.
The javac diagnostics wrapper project can post-process the javac diagnostics output into other formats, such as the LSP (Language Server Protocol) JSON style.
Project Lombok (https://projectlombok.org/) is a library that generates getter, setter, and builder methods, among other features. For example, if you declare a field:
@Getter @Setter
private @Regex String role;
then Lombok will generate getter and setter methods:
public @Regex String getRole() { return role; }
public void setRole(@Regex String role) { this.role = role; }
As illustrated in the example above, Lombok copies type annotations from fields to generated methods, when the user writes Lombok’s @Getter, @Setter, and @Builder annotations. Lombok does so only for certain type annotations (including all annotations in the
Checker Framework distribution); see variable BASE_COPYABLE_ANNOTATIONS in file HandlerUtil.java.
To make Lombok copy other type annotations from fields to generated code, add those type annotations to the lombok.copyableAnnotations configuration key in your lombok.config file. For example:
lombok.copyableAnnotations += my.checker.qual.MyTypeAnnotation
Directory docs/examples/lombok contains an example Gradle project that augments the configuration key.
If you run the Checker Framework and Lombok in the same javac invocation, the Checker Framework cannot type-check a class that contains Lombok annotations. The way that Lombok changes the class prevents the Checker Framework from seeing any of the class. (The Checker Framework works
fine on classes that do not contain Lombok annotations, including if they call Lombok-generated code.)
Therefore, you must run the Checker Framework in a postpass after the javac that runs Lombok has completed. Use the Delombok tool (distributed with Lombok) to generate Java source code, then run the
Checker Framework on that. The Checker Framework Gradle plugin does this for you automatically.
The Lombok Maven Plugin can be used to delombok source code for Maven projects. The Lombok Maven Plugin FAQ discusses how to configure the plugin when code using Lombok is mixed with standard Java code. Once the Lombok Maven plugin is configured correctly, the Checker Framework Maven configuration can be added independently.
If you use the Maven tool, then you can enable Checker Framework checkers by following the instructions below. These instructions have been tested with Maven 3.9.3, and they should work with any 3.x version of Maven.
See the directory docs/examples/MavenExample/ for examples of the use of Maven build files. This example can be used to verify that Maven is correctly downloading the Checker Framework from the Maven Central Repository and executing it.
Please note that the -AoutputArgsToFile command-line option (see Section 37.12.5) and shorthands for built-in checkers (see Section 2.2.4) are not available when following these instructions. Both these features are available only when a checker is launched via checker.jar such as when $CHECKERFRAMEWORK/checker/bin/javac
is run. The instructions in this section bypass checker.jar and cause the compiler to run a checker as an annotation processor directly.
Debugging your Maven configuration can be tricky because of a bug in maven-compiler-plugin versions before 3.10.1, and still affecting some users with 3.11.0: Maven does not report annotation processor
exceptions, even when the -X command-line argument is passed.
1. Declare a dependency on the Checker Framework artifacts from Maven Central. Find the existing <dependencies> section (not within <dependencyManagement>, but somewhere else) and add the following new <dependency> items:
<dependencies>
... existing <dependency> items ...
<!-- Annotations from the Checker Framework: nullness, interning, locking, ... -->
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>4.3.0</version>
</dependency>
</dependencies>
Periodically update to the most recent version, to obtain the latest bug fixes and new features:
mvn versions:use-latest-versions -Dincludes="org.checkerframework:*"
2. Direct the Maven compiler plugin to use the desired checkers by creating a new profile as shown below (the example uses the Nullness Checker).
If your POM file does not already use the maven-compiler-plugin plugin, you can use the text below as is. If your POM file already uses the plugin, copy and edit the first profile to incorporate the existing configuration. If the existing configuration uses proc:none, you need to remove
that or comment it out.
<profiles>
<profile>
<id>checkerframework</id>
<!-- If you omit the activation block, run mvn with "-P checkerframework" to run checkers. -->
<activation>
<jdk>[17,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<fork>true</fork> <!-- Must fork or else JVM arguments are ignored. -->
<annotationProcessorPaths>
<path>
<groupId>org.checkerframework</groupId>
<artifactId>checker</artifactId>
<version>4.3.0</version>
</path>
</annotationProcessorPaths>
<annotationProcessors>
<!-- Add all the checkers you want to enable here -->
<annotationProcessor>org.checkerframework.checker.nullness.NullnessChecker</annotationProcessor>
</annotationProcessors>
<compilerArgs>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
<arg>-Xmaxerrs</arg>
<arg>10000</arg>
<arg>-Xmaxwarns</arg>
<arg>10000</arg>
<!-- <arg>-Awarns</arg> --> <!-- -Awarns turns type-checking errors into warnings. -->
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
If you wish to run checkers while compiling your source code but not your tests, wrap the <configuration>...</configuration> within
<executions>
<execution>
<id>default-compile</id>
...
</execution>
</executions>
To compile without using the Checker Framework, pass -P ’!checkerframework’ on the Maven command line.
Warning: adding
<compilerArgs> ... <arg>-verbose</arg>
may suppress warnings from the stub parser.
If you use animal-sniffer-maven-plugin, you must upgrade to version 1.20 or later. Versions 1.19 and earlier are broken. If necessary, you can disable animal sniffer by modifying your POM file’s properties:
<properties> ... existing <property> items ... <!-- Needed for animal-sniffer-maven-plugin version 1.19 which is broken (version 1.20 is fixed). --> <animal.sniffer.skip>true</animal.sniffer.skip> </properties>
To use a locally-built version of the Checker Framework, first run:
./gradlew publishToMavenLocal
Then use the Maven instructions, but modify the version number for the Checker Framework artifacts. Instead of 4.3.0, use the version number that is output when you run ./gradlew version.
If you get an error like “module jdk.compiler does not export com.sun.tools.javac.processing to unnamed module” (discussed in Section 39.7.1) even though your pom.xml contains lines
such as
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
then perhaps you have omitted this line from your pom.xml:
<fork>true</fork> <!-- Must fork or else JVM arguments are ignored. -->
There are two approaches to running a checker in NetBeans: modifying the project properties, or using a custom Ant target.
Note: The “compile and save” action in NetBeans 8.1 automatically runs the Checker Framework, but this functionality has not yet been incorporated into NetBeans 8.2. Additionally, JDK annotations are currently unavailable on NetBeans 8.1 and 8.2.
1. Add the Checker Framework libraries to your project’s library. First, right click on the project in the Projects panel, and select “Properties” in the drop-down menu. Then, in the “Project Properties” window, navigate to the “Libraries” tab.
2. Add checker-qual.jar to the compile-time libraries. To do so, select the “Compile” tab, click the “Add JAR/Folder” button on the right and browse to add $CHECKERFRAMEWORK/checker/dist/checker-qual.jar.
3. Add checker.jar to the processor-path libraries. To do so, select the “Processor” tab, click the “Add JAR/Folder” button on the right and browse to add $CHECKERFRAMEWORK/checker/dist/checker.jar.
4. Enable annotation processor underlining in the editor. Go to “Build>Compiling” and check the box “Enable Annotation Processing”, and under that, “Enable Annotation Processing in Editor”.
5. Add the checker to run, by clicking “Add” next to the box labeled “Annotation Processors” and enter the fully qualified name of the checker (for example, org.checkerframework.checker.nullness.NullnessChecker) and click “OK” to add.
The selected checker should be run on the project either on a save (if Compile on Save is enabled), or when the project is built, and annotation processor output will appear in the editor.
1. Set the cfJavac property:
<property environment="env"/>
<property name="checkerframework" value="${env.CHECKERFRAMEWORK}" />
<condition property="cfJavac" value="javac.bat" else="javac">
<os family="windows" />
</condition>
<presetdef name="cf.javac">
<javac fork="yes" executable="${checkerframework}/checker/bin/${cfJavac}" >
<compilerarg value="-version"/>
<compilerarg value="-implicit:class"/>
</javac>
</presetdef>
2. Override the -init-macrodef-javac-with-processors target to use cf.javac instead of javac and to run the checker. In this example, a nullness checker is run:
<target depends="-init-ap-cmdline-properties" if="ap.supported.internal"
name="-init-macrodef-javac-with-processors">
<echo message = "${checkerframework}"/>
<macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${src.dir}" name="srcdir"/>
<attribute default="${build.classes.dir}" name="destdir"/>
<attribute default="${javac.classpath}" name="classpath"/>
<attribute default="${javac.processorpath}" name="processorpath"/>
<attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/>
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="${javac.debug}" name="debug"/>
<attribute default="${empty.dir}" name="sourcepath"/>
<attribute default="${empty.dir}" name="gensrcdir"/>
<element name="customize" optional="true"/>
<sequential>
<property location="${build.dir}/empty" name="empty.dir"/>
<mkdir dir="${empty.dir}"/>
<mkdir dir="@{apgeneratedsrcdir}"/>
<cf.javac debug="@{debug}" deprecation="${javac.deprecation}"
destdir="@{destdir}" encoding="${source.encoding}"
excludes="@{excludes}" fork="${javac.fork}"
includeantruntime="false" includes="@{includes}"
source="${javac.source}" sourcepath="@{sourcepath}"
srcdir="@{srcdir}" target="${javac.target}"
tempdir="${java.io.tmpdir}">
<src>
<dirset dir="@{gensrcdir}" erroronmissingdir="false">
<include name="*"/>
</dirset>
</src>
<classpath>
<path path="@{classpath}"/>
</classpath>
<compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
<compilerarg line="${javac.profile.cmd.line.arg}"/>
<compilerarg line="${javac.compilerargs}"/>
<compilerarg value="-processorpath"/>
<compilerarg path="@{processorpath}:${empty.dir}"/>
<compilerarg line="${ap.processors.internal}"/>
<compilerarg line="${annotation.processing.processor.options}"/>
<compilerarg value="-s"/>
<compilerarg path="@{apgeneratedsrcdir}"/>
<compilerarg line="${ap.proc.none.internal}"/>
<compilerarg line="-processor org.checkerframework.checker.nullness.NullnessChecker"/>
<compilerarg line="-Xmaxerrs 10000"/>
<compilerarg line="-Xmaxwarns 10000"/>
<customize/>
</cf.javac>
</sequential>
</macrodef>
</target>
<target name="-post-jar">
</target>
When Build and Clean Project is used, the output of the checker will now appear in the build console. However, annotation processor output will not appear in the editor.
sbt is a build tool for Scala, Java, and more.
Adjust the -processor command-line argument for the processor(s) you wish to run (see Section 2.2).
javacOptions ++= Seq(
"-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
"-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
"-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar",
"-cp $CHECKERFRAMEWORK/checker/dist/checker-qual.jar",
"-processor org.checkerframework.checker.nullness.NullnessChecker"
)
For compiling modularized code:
javacOptions ++= Seq(
"-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
"-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
"-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
"-processorpath $CHECKERFRAMEWORK/checker/dist/checker.jar",
"--module-path $CHECKERFRAMEWORK/checker/dist/checker-qual.jar",
"-processor org.checkerframework.checker.nullness.NullnessChecker"
)
tIDE, an open-source Java IDE, supports the Checker Framework. You can download it from https://sourceforge.net/projects/tide/.
A type inference tool infers type annotations for a program’s method signatures and fields, so that the programmer does not need to manually annotate the program’s source code. Section 35.1 lists type inference tools.