JDK-8225377 : type annotations are not visible to javac plugins across compilation boundaries
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 13,21
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2019-06-05
  • Updated: 2025-04-18
  • Resolved: 2023-11-03
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 17 JDK 21 JDK 22
11.0.23Fixed 17.0.11Fixed 21.0.2Fixed 22 b23Fixed
Related Reports
CSR :  
Cloners :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Sub Tasks
JDK-8323094 :  
Description
javac fails to associate type annotations with TypeMirrors for some symbols loaded from the classpath, which prevents plugins from accessing those annotations across compilation boundaries. The annotations are present if the same symbol is compiled from source in the compilation where the plugin runs.

=== ./test/B.java
abstract class B extends A {}
=== ./test/A.java
import java.lang.annotation.ElementType;
import java.util.List;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
@interface TypeAnnotation {}

abstract class A implements List<@TypeAnnotation String> {}
=== ./plugin/p/P.java
package p;

import com.sun.source.util.JavacTask;
import com.sun.source.util.Plugin;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;

public class P implements Plugin {

  @Override
  public String getName() {
    return "P";
  }

  @Override
  public void init(JavacTask javacTask, String... strings) {
    javacTask.addTaskListener(
        new TaskListener() {
          @Override
          public void finished(TaskEvent e) {
            if (e.getKind() != TaskEvent.Kind.ENTER) {
              return;
            }
            TypeElement b = javacTask.getElements().getTypeElement("B");
            for (TypeMirror i :
                ((TypeElement) ((DeclaredType) b.getSuperclass()).asElement()).getInterfaces()) {
              System.err.printf("%s %s\n", i, i.getAnnotationMirrors());
            }
          }
        });
  }
}
=== ./plugin/module-info.java
module p { 
  requires transitive jdk.compiler; 
  provides com.sun.source.util.Plugin with p.P; 
} 
===

$ javac $(find plugin -name '*.java')

# when both compilation units are compiled from source, the type annotations are visible

$ javac --processor-module-path plugin -Xplugin:P test/A.java test/B.java 
java.util.List<@TypeAnnotation java.lang.String> 
java.util.List<@TypeAnnotation java.lang.String> 

# when 'A' is loaded from the classpath, the type annotations on its supertype are not visible

$ javac --processor-module-path plugin -Xplugin:P -classpath test test/B.java 
java.util.List<java.lang.String> 

Comments
For posterity: This got backed out of JDK 21.0.2 with JDK-8322883 and re-done in 21.0.8 with JDK-8341779
18-04-2025

As advised I have written up relnotes (https://bugs.openjdk.org/browse/JDK-8323094) and a retroactive CSR (https://bugs.openjdk.org/browse/JDK-8323093) for this change, feedback is welcome.
08-01-2024

Understood, thanks. Are there other public issues besides JDK-8322641 you can share?
23-12-2023

Liam, this fix is going to be backed out of 21.0.2 as the change in behaviour is causing issues (see JDK-8322641), you may also want to reconsider the change for 11 and 17. There needs to be a release-note for 22 - and perhaps a retroactive CSR should be considered.
22-12-2023

Fix Request 11u Backporting this patch fixes a bug preventing type use annotations for elements loaded from class files from appearing in the javax.lang.model API. This bug is preventing adoption of type use annotations. Risk is low. Annotation processors will be able to access new type annotations for elements loaded from the classpath. This can have observable side effects, for example if an annotation processor includes `TypeMirror#toString` output in diagnostics or generated code that output may now include additional annotations. The patch did not apply cleanly. I resolved a merge conflict with the imports, and due to changes to the internal TypeMetadata API for JDK-8303820. The modified test fails without the patch, and passes with it. The patch passes langtools:tier1 tests. I also built Google's code with this patch applied to validate it. Backport requires follow up issue JDK-8320001.
29-11-2023

Fix Request 17u Backporting this patch fixes a bug preventing type use annotations for elements loaded from class files from appearing in the javax.lang.model API. This bug is preventing adoption of type use annotations. Risk is low. Annotation processors will be able to access new type annotations for elements loaded from the classpath. This can have observable side effects, for example if an annotation processor includes `TypeMirror#toString` output in diagnostics or generated code that output may now include additional annotations. The patch did not apply cleanly, I resolved one merge conflict due to changes to the internal TypeMetadata API for JDK-8303820. The modified test fails without the patch, and passes with it. The patch passes langtools:tier1 tests. I also built Google's code with this patch applied to validate it. Backport requires follow up issue JDK-8320001.
29-11-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jdk11u-dev/pull/2272 Date: 2023-11-08 01:31:36 +0000
08-11-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jdk17u-dev/pull/1952 Date: 2023-11-08 00:58:28 +0000
08-11-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jdk21u/pull/329 Date: 2023-11-06 22:10:08 +0000
06-11-2023

Fix Request JDK 21u Backporting this patch fixes a bug preventing type use annotations for elements loaded from class files from appearing in the javax.lang.model API. This bug is preventing adoption of type use annotations. Risk is low. Annotation processors will be able to access new type annotations for elements loaded from the classpath. This can have observable side effects, for example if an annotation processor includes `TypeMirror#toString` output in diagnostics or generated code that output may now include additional annotations. The patch applies cleanly to 21u. The backport passes the langtools:tier1 tests. I also built Google's code with this patch applied to validate it.
06-11-2023

Changeset: de6667cf Author: Liam Miller-Cushon <cushon@openjdk.org> Date: 2023-11-03 17:31:05 +0000 URL: https://git.openjdk.org/jdk/commit/de6667cf11aa59d1bab78ae5fb235ea0b901d5c4
03-11-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/16407 Date: 2023-10-27 21:01:54 +0000
27-10-2023

I think the underlying issue is the same for both -Xplugin style plugins and annotation processors. I did some investigation last year and left some notes in these compiler-dev posts: * https://mail.openjdk.org/pipermail/compiler-dev/2022-March/019122.html * https://mail.openjdk.org/pipermail/compiler-dev/2022-April/019375.html
16-02-2023

another test case provided by Liam, having: === A.java === import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE_USE}) public @interface A {} === B.java === public final class B { public static final void c(@A String s) {} } === P.java === 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.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.tools.Diagnostic; @SupportedAnnotationTypes("*") public class P extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { Elements elements = processingEnv.getElementUtils(); Element e = elements.getTypeElement("B"); ExecutableElement c = (ExecutableElement) e.getEnclosedElements().get(1); VariableElement s = c.getParameters().get(0); TypeMirror sAsType = s.asType(); processingEnv .getMessager() .printMessage( Diagnostic.Kind.NOTE, String.format( "%s.%s.%s has annotations [%s]", e, c, s, sAsType.getAnnotationMirrors())); return false; } } $ javac -processor P -implicit:none -sourcepath : -parameters A.java B.java Note: B.c(java.lang.@A String).s has annotations [@A] Note: B.c(java.lang.@A String).s has annotations [@A] $ javac -processor P -implicit:none -sourcepath : -parameters A.java Note: B.c(java.lang.String).s has annotations [] Note: B.c(java.lang.String).s has annotations []
16-02-2023