Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8194900

Inconsistent handling of recoverable errors during annotation processing



    • Type: Bug
    • Status: Open
    • Priority: P4
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: tools


      The following example contains a simple class hierarchy where A extends B, and B extends C. B is generated by an annotation processor.

      Using `@I(B.class)` in the compilation succeeds: a recoverable 'cant.resolve' error is reported, and fixed when B is generated during the first annotation processing round.

      Using `@I(A.class)` does not succeed: checking the constraint on I.value requires A's super type, which hasn't been generated yet, resulting in a non-recoverable inconvertible.types error.

      This is potentially surprising, since in both cases the annotation depends on the missing type, and the example that compiles depends more directly on the missing type.

      Is this working as intended?

      === P.java
      import static java.nio.charset.StandardCharsets.UTF_8;

      import java.io.IOError;
      import java.io.IOException;
      import java.io.OutputStream;
      import java.util.Set;
      import javax.annotation.processing.AbstractProcessor;
      import javax.annotation.processing.RoundEnvironment;
      import javax.annotation.processing.SupportedAnnotationTypes;
      import javax.lang.model.SourceVersion;
      import javax.lang.model.element.TypeElement;

      public final class P extends AbstractProcessor {

        public SourceVersion getSupportedSourceVersion() {
          return SourceVersion.latestSupported();

        boolean first = true;

        public final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
          if (first) {
            try (OutputStream os = processingEnv.getFiler().createSourceFile("B").openOutputStream()) {
              os.write("class B extends C {}".getBytes(UTF_8));
            } catch (IOException e) {
              throw new IOError(e);
            first = false;
          return true;
      === T.java
      @interface I {
        Class<? extends C> value() default C.class;

      class C {}

      // generated:
      // class B extends C {}

      class A extends B {}

      // @I(B.class) // OK
      @I(A.class) // error: incompatible types: Class<A> cannot be converted to Class<? extends C>
      class T {}

      $ javac -fullversion -processor P T.java
      javac full version "9.0.1+11"
      T.java:10: error: cannot find symbol
      class A extends B {}
        symbol: class B
      T.java:13: error: incompatible types: Class<A> cannot be converted to Class<? extends C>
      @I(A.class) // error: incompatible types: Class<A> cannot be converted to Class<? extends C>
      2 errors




            Unassigned Unassigned
            cushon Liam Miller-Cushon
            0 Vote for this issue
            4 Start watching this issue