Calvin-L / checker-framework-plus-lombok

A demonstration of the Checker Framework and Lombok working in harmony

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Checker Framework + Lombok, in perfect (?) harmony

Despite claims to the contrary, the Checker Framework is actually compatible with Lombok. However, a quirk of how Lombok works means that the order of annotation processors matters, and the Checker Framework processors must be listed first. This is confusing because the Checker Framework processors are indended to run last.

This repo demonstrates a functioning project that combines the two.

Install Maven and run mvn verify to compile the project. It should fail (expected!) with

[ERROR] /Users/cloncaric/src/checker-framework-plus-lombok/src/main/java/calvin/example/Basics.java:[15,19] error: [dereference.of.nullable] dereference of possibly-null reference t.getVal()

This error indicates that the Checker Framework is working. It shows that the Checker Framework was able to report an error about a misuse of a Lombok-generated method.

You can also demonstrate this with ./compile-by-hand.sh, which shows a simple invocation of javac without all of Maven's machinery.

Why the Checker Framework must be listed first

Background: annotation processing

Javac runs annotation processors before typechecking. Each processor advertises a set of class-level annotations that it is interested in using the @SupportedAnnotationTypes annotation or by overriding getSupportedAnnotationTypes. Javac will only run a processor on a class if that class has a class-level annotation that the processor is interested in. A processor can advertise interest in all annotations using @SupportedAnnotationTypes("*"), in which case it will run on every class, even if the class has no class-level annotations.

One strange quirk of the annotation processing machinery is that if there are two processors with @SupportedAnnotationTypes("*") and the first of them returns true from its process method, then the second is not guaranteed to run.

Background: how Lombok processes annotations

Lombok's annotation processor adversises interest in all annotations. Furthermore, its process method returns true when all the annotations on the class are Lombok annotations and there is at least one of them.

Background: how the Checker Framework processes annotations

The Checker Framework annotation processors advertise interest in all annotations. Their process methods always return false. However, because javac runs annotation processors before typechecking, their process methods do not actually run the respective checker. Instead, they register a callback to be invoked later, after typechecking has completed.

Summing up

The unfortunate combination of background facts leads to incorrect behavior for classes that have class-level Lombok annotations and no other annotations, e.g.

@lombok.Slf4j
public class Basics {
    ...
}

For such classes, Lombok returns true from its process method, preventing the Checker Framework from running at all. By running the Checker Framework first, it has a chance to register its callback before Lombok ends javac's annotation processing. The callback will then run after typechecking, once Lombok has finished its rewriting work.

A bug in javac revealed by Lombok's ClaimingProcessor

There is a second issue related to a bug in javac (JDK-8312460), although the primary problem is the fact that Lombok's process method returns true in some cases (see above).

Lombok includes a second annotation processor called ClaimingProcessor that (1) advertises interest in only the lombok.* annotations and (2) returns true in its process method (but does nothing else). This annotation processor is an ideal, well-formed processor that does not inherit any of the quirks of @SupportedAnnotationTypes("*") processors.

Due to JDK-8312460, even if the Lombok team patched their process method to always return false, ClaimingProcessor would still prevent the Checker Framework from running. See the JDK-8312460 description linked above for more information.

Unfortunately the ticket was closed as "won't fix", although I suspect that the javac developers are very busy and did not fully understand this entirely-too-subtle issue.

About

A demonstration of the Checker Framework and Lombok working in harmony

License:MIT License


Languages

Language:Shell 80.3%Language:Java 19.7%