Releases: xzel23/cabe
v4.1
Introduce 'strict mode'
Introduced a strict mode option to enforce the equals(Object) contract.
When enabled, instrumentation will fail if equals(Object) is overridden with
a non-nullable parameter. In non-strict mode (default), a warning is logged
and the parameter is automatically treated as nullable.
v4.0.2
v4.0.1
Correctness check for equals() implementations
Added a check that equals overrides are declared correctly, i.e., the argument must be nullable for the equals() contract "x.equals(null) must return false" to be fulfilled. If the contract is violated, instrumentation will be aborted with an error message.
Rationale
public boolean equals(Object) must be implemented in a way such that x.equals(null) evaluates to false. Therefore, equals(null) must neither throw a NullPointerException nor an AssertionError. Many IDEs offer automatic equals() and hashCode() generation but do not take into account when this is done in a @NullMarked context, resulting in hard to find errors.
This check is only performed for equals() implementations that override Object.equals(Object), not for overloads using a different type.
Fix: instrumentation failed when optional modules were not on the module path
Fixed an issue where instrumentation failed when modules with optional dependencies were on the module path. (#22)
Improve compatibility with Gradle build and configuration caches
The plugin was refactored for better compatibility with the Gradle configuration and build caches.
Maven example is built in build.sh
The Maven example previously was not automatically built and so bugs could go undetected. The example is now automatically built. In addition, the correct plugin version is now determined automatically and does not to have updated manually anymore.
v4-beta2
Correctness check for equals() implementations
Added a check that equals overrides are declared correctly, i.e., the argument must be nullable for the equals() contract "x.equals(null) must return false" to be fulfilled. If the contract is violated, instrumentation will be aborted with an error message.
Rationale
public boolean equals(Object) must be implemented in a way such that x.equals(null) evaluates to false. Therefore, equals(null) must neither throw a NullPointerException nor an AssertionError. Many IDEs offer automatic equals() and hashCode() generation but do not take into account when this is done in a @NullMarked context, resulting in hard to find errors.
This check is only performed for equals() implementations that override Object.equals(Object), not for overloads using a different type.
Fix: instrumentation failed when optional modules were not on the module path
Fixed an issue where instrumentation failed when modules with optional dependencies were on the module path. (#22)
Improve compatibility with Gradle build and configuration caches
The plugin was refactored for better compatibility with the Gradle configuration and build caches.
Maven example is built in build.sh
The Maven example previously was not automatically built and so bugs could go undetected. The example is now automatically built. In addition, the correct plugin version is now determined automatically and does not to have updated manually anymore.
v3.3.0
- The Gradle plugin was updated for full Gradle 9 compatibility.
- The Gradle plugin now supports the Gradle build and configuration caches allowing for faster builds.
- Rewrote much of the documentation, added documentation for the Maven plugin
- The deployment is now done automatically using GitHub actions.
- Migrated from Sonatype Nexus OSS-RH (closed on June 30 2025) to the Maven Central Publisher Portal.
v3.1.0
v3.1.0
This release includes some important fixes and improvements. It is recommended to upgrade from earlier versions.
Use a Java 17 Toolchain for Building
The build was changed to use a Toolchain instead of relying on a compatible JDK to be present and configured.
Fix: Compilation without configured JavaFX
Some of the regression tests use JavaFX classes. This has always worked for me because I usually use either Liberica Full or
Azul JDKFX as my standard environment. I recently changed that while working on some issues of another project that required
a Temurin JDK to reproduce (that's because the Temurin build does not ship jmods anymore as they should no longer be needed
for jlink (see JEP 493 for details). As a result, the plugin build failed due to the tests not compiling.
This has been fixed by pulling in the required JavaFX classes for the compilation of tests.
Fix: Annotations on Enum Constructor Parameters
A bug concerning annotated enum constructor parameters has been fixed.
This minimal code shows the problem:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Parameter;
public class Main {
public static void main(String[] args) throws Exception {
// Get the enum constructor
var constructor = E.class.getDeclaredConstructors()[0];
Parameter[] parameters = constructor.getParameters();
System.out.println("Constructor signature: " + constructor);
System.out.println("Parameter count: " + parameters.length);
for (int i = 0; i < parameters.length; i++) {
Parameter param = parameters[i];
var annotations = param.getAnnotatedType().getDeclaredAnnotations();
System.out.printf("Parameter[%d]: %s %s - annotations: %s%n",
i, param.getType().getSimpleName(), param.getName(),
annotations.length > 0 ? annotations[0] : "none");
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface Annotation {
}
enum E {
VALUE1(42, "hello"),
VALUE2(99, null);
E(int number, @Annotation String text) {
}
}Here, the parameter text to the enumeration constructor is @Nullable, so this should be fine. Due to what I assume is a
bug in the Java compiler (I am still waiting for confirmation from the OpenJDK devs), the annotation is not visible on the
annotated type of the parameter. What happens is that the compiler introduces two synthetic parameters, ordinal and
name, and reports the first synthetic parameter as annotated with @Nullable.
This has been reported to OpenJDK and a workaround was created for the CabeProcessor class. This workaround
is only applied when the presence of the bug is confirmed, so things won't break once the compiler is fixed or when
another compiler is used (I haven't checked if for example ECJ handles this correctly).
v3.0.1
v3.0.0
Changes since the release candidate:
- updated the help text of the command line application
- updated documentation
- documentation is now published on GitHub Pages
v3.0-rc
- use JSpecify annotations; dropped custom annotations
- support annotations on module
- simplified plugin configuration in build file
- requires Java 17 to run
- support Java versions up to Java 23
- documentation available on GitHub pages (https://xzel23.github.io/cabe/index.html)
Cabe 3 Beta 1 - Cabe switches to JSpecify annotations
The cabe-annotations module has been removed. Instead of the custom annotations, use JSpecify annotations, i.e., @NullMarked/@NullUnmarked for modules, packages, and classes and @NonNull/@Nullable for parameters. Consult the Nullness User Guide to learn how to annotate your code.
Cabe as before supports injecting checks on method and constructor parameters, including auto-generated canonical record constructors. Checks for method return values and local variables are not supported.
The Config class has been renamed to configuration. The @NullMarked annotation should work on modules, packages, and classes, but more test cases are needed.