How to prevent Eclipse from generating type annotations when assigning expression to local variable

Viewed 404

Assuming I have Java code like this:

package test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

public class Test {

    @NotNull
    public static Object object() {
        return new Object();
    }

    public static void test() {
        object();
    }
}

@Target({ ElementType.METHOD, ElementType.TYPE_USE })
@interface NotNull {}

When I use the quick fix (Ctrl+1) action "Assign statement to new local variable" on the "object();" call, I'm getting this:

public static void test() {
    @NotNull
    Object object = object();
}

I don't want the annotation in my client Java code as it's just noise for me. How can I prevent it from being generated all the time, and get this instead?

public static void test() {
    Object object = object();
}

All of this only happens when the annotation has @Target(TYPE_USE), and only when org.eclipse.jdt.core.compiler.compliance=8 or later in .settings/org.eclipse.jdt.core.prefs

For the record, I've also filed enhancement request 565463.

(The real world API is declaring such annotations for better Kotlin interoperability (the JetBrains annotations are used), as Kotlin can use it to distinguish between T and T?, and avoid T!)

2 Answers

The @NotNull, unwanted in your case, will be added if and only if annotation-based null analysis is not enabled or misconfigured.

In Project > Properies: Java > Compiler in the section Null analysis:

  1. Make sure the checkbox Enable annotation-based null analysis is checked
  2. Instead of the checkbox Use default annotation for null specification being checked, click the Configure... link and configure test.NotNull (or org.jetbrains.annotations.NotNull) as 'NonNull' annotation

Please note, only ElementType.TYPE_USE annotations will be added. For methods annotated with an ElementType.TYPE_USE annotation (which cannot be added to void methods), the annotation refers to the return type, not to the method itself. In other words, you get an annotated object when calling such a method. For me, to add an Use of a type annotations to the type of a variable is the expected behavior.

It only becomes unclear for annotations like in your case that have both targets, ElementType.TYPE_USE and ElementType.METHOD. Does the annotation refer to the return type (so it make sense to add it) or does the method refer to the method itself (and therefore it should not be added)?

Please not also, in contrast to org.jetbrains.annotations.NotNull org.eclipse.jdt.annotation.NonNull has no target ElementType.METHOD. How can a method declaration be null? What is the meaning of a @NotNull method? According to the Javadoc of org.jetbrains.annotations.NotNull ("... null value is forbidden to return (for methods)..."), the target ElementType.METHOD seems to be added by mistake here, since it does not refer to the method declaration.

While the question is generic, the use-case behind the question is the jOOQ API, which starts adding the Jetbrains annotations in jOOQ 3.14 (see #6244) for better Kotlin interoperability, e.g.

interface ResultQuery<R> {
  @NotNull Result<R> fetch();
  @Nullable R fetchOne();
}

In the case of jOOQ, the dependency on the annotations can be made optional/provided in Maven, or static in JPMS, such that ordinary Java client code doesn't see the annotation by default:

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <scope>provided</scope>
    <optional>true</optional>
</dependency>

The Kotlin compiler can still see it nevertheless:

val q: ResultQuery<R> = ...
q.fetch() // Result<R!>
q.fetchOne() // R?

If a third party out of your control is introducing the dependency, it can be excluded like this:

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    
    <exclusions>
        <exclusion>
            <groupId>org.jetbrains</groupId>
            <artifactId>annotations</artifactId>
        </exclusion>
    </exclusions>
</dependency>
Related