Git Product home page Git Product logo

checkerframework-gradle-plugin's Introduction

Gradle Checker Framework Plugin

License Build Status

This plugin configures JavaCompile tasks to use the Checker Framework for pluggable type-checking.

Download

Add the following to your build.gradle file:

plugins {
    // Checker Framework pluggable type-checking
    id 'org.checkerframework' version '0.5.0'
}

apply plugin: 'org.checkerframework'

The org.checkerframework plugin modifies existing Java compilation tasks. You should apply it after whatever plugins introduce your Java compilation tasks (usually the java or java-library plugin for non-Android builds).

Configuration

Configuring which checkers to use

The checkerFramework.checkers property lists which checkers will be run.

For example:

checkerFramework {
  checkers = [
    'org.checkerframework.checker.nullness.NullnessChecker',
    'org.checkerframework.checker.units.UnitsChecker'
  ]
}

For a list of checkers, see the Checker Framework Manual.

Providing checker-specific options to the compiler

You can set the checkerFramework.extraJavacArgs property in order to pass additional options to the compiler when running a typechecker.

For example, to use a stub file:

checkerFramework {
  extraJavacArgs = [
    '-Werror',
    '-Astubs=/path/to/my/stub/file.astub'
  ]
}

Configuring third-party checkers

To use a third-party typechecker (i.e. one that is not distributed with the Checker Framework), add a dependency to the checkerFramework dependency configuration.

For example, to use the Glacier immutability checker:

dependencies {
  ...
  checkerFramework 'edu.cmu.cs.glacier:glacier:0.1'
}

You should also use a checkerFramework dependency for anything needed by a checker you are running. For example, if you are using the Subtyping Checker with custom type qualifiers, you should add a checkerFramework dependency referring to the definitions of the custom qualifiers.

Specifying a Checker Framework version

Version 0.5.1 of this plugin uses Checker Framework version 3.4.0 by default. Anytime you upgrade to a newer version of this plugin, it might use a different version of the Checker Framework.

You can use a Checker Framework version that is different than this plugin's default. If you want to use Checker Framework version 3.1.0, then you should add the following text to build.gradle, after apply plugin: 'org.checkerframework':

dependencies {
  compileOnly 'org.checkerframework:checker-qual:3.1.0'
  testCompileOnly 'org.checkerframework:checker-qual:3.1.0'
  checkerFramework 'org.checkerframework:checker:3.1.0'
}

You can also use a locally-built version of the Checker Framework:

// To use a locally-built Checker Framework, run gradle with "-PcfLocal".
if (project.hasProperty("cfLocal")) {
  def cfHome = String.valueOf(System.getenv("CHECKERFRAMEWORK"))
  dependencies {
    compileOnly files(cfHome + "/checker/dist/checker-qual.jar")
    testCompileOnly files(cfHome + "/checker/dist/checker-qual.jar")
    checkerFramework files(cfHome + "/checker/dist/checker.jar")
  }
}

Other options

  • By default, the plugin applies the selected checkers to all JavaCompile targets.

    Here is how to prevent checkers from being applied to test targets:

    checkerFramework {
      excludeTests = true
    }

    The check for test targets is entirely syntactic: this option will not apply the checkers to any task whose name includes "test", ignoring case.

  • If you encounter errors of the form zip file name too long when configuring your Gradle project, you can use the following code to skip this plugin's version check, which reads the manifest file of the version of the Checker Framework you are actually using:

    checkerFramework {
      skipVersionCheck = true
    }

Multi-project builds

In most projects with subprojects, the top-level project is not a Java project. You should not apply the plugin to such a non-Java project. Instead, move all Checker Framework configuration (the checkerFramework block and any dependencies) into a subprojects block, and do not apply the plugin to the top-level project. For example:

plugins {
  id 'org.checkerframework' version '0.5.1' apply false
}

subprojects { subproject ->
  apply plugin: 'org.checkerframework'

  checkerFramework {
    checkers = ['org.checkerframework.checker.index.IndexChecker']
  }
  dependencies {
    checkerFramework 'org.checkerframework:checker:3.4.0'
    implementation 'org.checkerframework:checker-qual:3.4.0'
  }
}

If the top-level project is a Java project that you wish to typecheck, follow these instructions but replace the 'subprojects' block with the 'allprojects' block.

Alternately, apply the plugin in the build.gradle in each subproject as if it were a stand-alone project. You must do this if you require different configuration for different subprojects (for instance, if you want to run different checkers).

Incompatibility with Error Prone

Error Prone uses the Checker Framework's dataflow analysis library. Unfortunately, Error Prone uses an old version of the library, so you cannot use both Error Prone and the current Checker Framework (because each one depends on a different version of the library).

You can resolve this via a switch that causes your build to use either Error Prone or the Checker Framework, but not both. Here is an example of a build that uses both:

plugins {
  id "net.ltgt.errorprone" version "1.1.1" apply false
  // To do Checker Framework pluggable type-checking (and disable Error Prone), run:
  // ./gradlew compileJava -PuseCheckerFramework=true
  id 'org.checkerframework' version '0.5.1' apply false
}

if (!project.hasProperty("useCheckerFramework")) {
    ext.useCheckerFramework = "false"
}
if ("true".equals(project.ext.useCheckerFramework)) {
  apply plugin: 'org.checkerframework'
} else {
  apply plugin: 'net.ltgt.errorprone'
}

def errorProneVersion = "2.3.4"
def checkerFrameworkVersion = "3.4.0"

dependencies {
  if ("true".equals(project.ext.useCheckerFramework)) {
    checkerFramework 'org.checkerframework:checker:' + checkerFrameworkVersion
    checkerFramework 'org.checkerframework:checker-qual:' + checkerFrameworkVersion
  } else {
    errorprone group: 'com.google.errorprone', name: 'error_prone_core', version: errorProneVersion
  }
}

if ("true".equals(project.ext.useCheckerFramework)) {
  checkerFramework {
    checkers = [
      'org.checkerframework.checker.interning.InterningChecker',
      'org.checkerframework.checker.signature.SignatureChecker'
    ]
  }
} else {
  // Configuration for the Error Prone linter.
  tasks.withType(JavaCompile).each { t ->
    if (!t.name.equals("compileTestInputJava") && !t.name.startsWith("checkTypes")) {
      t.toolChain ErrorProneToolChain.create(project)
      t.options.compilerArgs += [
        '-Xep:StringSplitter:OFF',
        '-Xep:ReferenceEquality:OFF' // use Interning Checker instead
      ]
    }
  }
}

Java 9+ compatibility

When running the plugin on a Java 9+ project that uses modules, you may need to add annotations to the module path. 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.

Then, add this line to the checkerFramework block to add the checker-qual.jar artifact (which only contains annotations) to the module path:

checkerFramework {
  extraJavacArgs = [
    '--module-path', compileOnly.asPath
  ]
}

Lombok compatibility

This plugin automatically interacts with the Lombok Gradle Plugin to delombok your source code before it is passed to the Checker Framework for typechecking. This plugin does not support any other use of Lombok.

By default, Lombok suppresses all warnings in the code it generates. If you want to typecheck the code that Lombok generates, use the suppressLombokWarnings configuration key:

checkerFramework {
  suppressLombokWarnings = false
}

Note that doing so will cause all tools (including Javac itself) to begin issuing warnings in the code that Lombok generates.

Using a locally-built plugin

You can build the plugin locally rather than downloading it from Maven Central.

To build the plugin from source, run ./gradlew build.

If you want to use a locally-built version of the plugin, you can publish the plugin to your local Maven repository by running ./gradlew publishToMavenLocal. In the build.gradle file for each project for which you want to use the locally-built plugin, make sure that mavenLocal() is the first entry in the repositories block within the buildscript block. A full example will look like this:

buildscript {
  repositories {
    mavenLocal()
    maven {
        url 'https://plugins.gradle.org/m2/'
    }
  }

  dependencies {
    classpath 'org.checkerframework:checkerframework-gradle-plugin:0.5.1'
  }
}

apply plugin: 'org.checkerframework'

JDK 8 vs JDK 9+ implementation details

The plugin attempts to automatically configure the Checker Framework on both Java 8 and Java 9+ JVMs, following the best practices in the Checker Framework manual. In particular:

  • If both the JVM and target versions are 8, it applies the Java 8 annotated JDK.
  • If the JVM version is 9+ and the target version is 8 (and the Checker Framework version is >= 2.11.0), use the Error Prone javac compiler.
  • If the JVM version is 9+, use the --add-opens option to javac.

Credits

This project started as a fork of a plugin built by jaredsburrows. Twitter Follow

License

Copyright (C) 2017 Jared Burrows, 2018-2020 Martin Kellogg

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.