changeset 57820:887b72d7bf86

8236210: javac generates wrong annotation for fields generated from record components Reviewed-by: mcimadamore
author vromero
date Thu, 23 Jan 2020 19:20:11 -0500
parents 84e3b673fe34
children 11e188a95589
files src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java src/jdk.compiler/share/classes/com/sun/tools/javac/code/SymbolMetadata.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java test/langtools/tools/javac/records/RecordCompilationTests.java
diffstat 5 files changed, 505 insertions(+), 64 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Thu Jan 23 15:52:54 2020 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Thu Jan 23 19:20:11 2020 -0500
@@ -29,6 +29,7 @@
 import java.lang.annotation.Inherited;
 import java.util.Collections;
 import java.util.EnumSet;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Callable;
@@ -58,6 +59,7 @@
 import com.sun.tools.javac.jvm.*;
 import com.sun.tools.javac.jvm.PoolConstant;
 import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
 import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
 import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
 import com.sun.tools.javac.tree.JCTree.Tag;
@@ -1274,6 +1276,9 @@
         /** the annotation metadata attached to this class */
         private AnnotationTypeMetadata annotationTypeMetadata;
 
+        /* the list of any of record components, only non empty if the class is a record
+         * and it has at least one record component
+         */
         private List<RecordComponent> recordComponents = List.nil();
 
         public ClassSymbol(long flags, Name name, Type type, Symbol owner) {
@@ -1471,15 +1476,24 @@
             return Flags.asModifierSet(flags & ~DEFAULT);
         }
 
-        public RecordComponent getRecordComponent(VarSymbol field, boolean addIfMissing) {
+        public RecordComponent getRecordComponent(VarSymbol field) {
             for (RecordComponent rc : recordComponents) {
                 if (rc.name == field.name) {
                     return rc;
                 }
             }
+            return null;
+        }
+
+        public RecordComponent getRecordComponent(JCVariableDecl var, boolean addIfMissing) {
+            for (RecordComponent rc : recordComponents) {
+                if (rc.name == var.name) {
+                    return rc;
+                }
+            }
             RecordComponent rc = null;
             if (addIfMissing) {
-                recordComponents = recordComponents.append(rc = new RecordComponent(PUBLIC, field.name, field.type, field.owner));
+                recordComponents = recordComponents.append(rc = new RecordComponent(var));
             }
             return rc;
         }
@@ -1735,14 +1749,18 @@
     public static class RecordComponent extends VarSymbol implements RecordComponentElement {
         public MethodSymbol accessor;
         public JCTree.JCMethodDecl accessorMeth;
+        private final List<JCAnnotation> originalAnnos;
 
         /**
          * Construct a record component, given its flags, name, type and owner.
          */
-        public RecordComponent(long flags, Name name, Type type, Symbol owner) {
-            super(flags, name, type, owner);
+        public RecordComponent(JCVariableDecl fieldDecl) {
+            super(PUBLIC, fieldDecl.sym.name, fieldDecl.sym.type, fieldDecl.sym.owner);
+            this.originalAnnos = fieldDecl.mods.annotations;
         }
 
+        public List<JCAnnotation> getOriginalAnnos() { return originalAnnos; }
+
         @Override @DefinedBy(Api.LANGUAGE_MODEL)
         @SuppressWarnings("preview")
         public ElementKind getKind() {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/SymbolMetadata.java	Thu Jan 23 15:52:54 2020 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/SymbolMetadata.java	Thu Jan 23 19:20:11 2020 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -267,25 +267,9 @@
         return lb.toList();
     }
 
-    private List<Attribute.TypeCompound> removeFromTypeCompoundList(List<Attribute.TypeCompound> l, Attribute.TypeCompound compound) {
-        ListBuffer<Attribute.TypeCompound> lb = new ListBuffer<>();
-        for (Attribute.TypeCompound c : l) {
-            if (c != compound) {
-                lb.add(c);
-            }
-        }
-        return lb.toList();
-    }
-
-    public void remove(Attribute.Compound compound) {
+    public void removeDeclarationMetadata(Attribute.Compound compound) {
         if (attributes.contains(compound)) {
             attributes = removeFromCompoundList(attributes, compound);
-        } else if (type_attributes.contains(compound)) {
-            type_attributes = removeFromTypeCompoundList(type_attributes, (TypeCompound)compound);
-        } else if (init_type_attributes.contains(compound)) {
-            init_type_attributes = removeFromTypeCompoundList(init_type_attributes, (TypeCompound)compound);
-        } else if (clinit_type_attributes.contains(compound)) {
-            clinit_type_attributes = removeFromTypeCompoundList(clinit_type_attributes, (TypeCompound)compound);
         } else {
             // slow path, it could be that attributes list contains annotation containers, so we have to dig deeper
             for (Attribute.Compound attrCompound : attributes) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Thu Jan 23 15:52:54 2020 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Thu Jan 23 19:20:11 2020 -0500
@@ -2908,7 +2908,7 @@
                  * the corresponding record component
                  */
                 ClassSymbol recordClass = (ClassSymbol) s.owner;
-                RecordComponent rc = recordClass.getRecordComponent((VarSymbol)s, false);
+                RecordComponent rc = recordClass.getRecordComponent((VarSymbol)s);
                 SymbolMetadata metadata = rc.getMetadata();
                 if (metadata == null || metadata.isEmpty()) {
                     /* if not is empty then we have already been here, which is the case if multiple annotations are applied
@@ -2917,34 +2917,93 @@
                     rc.appendAttributes(s.getRawAttributes().stream().filter(anno ->
                             Arrays.stream(getTargetNames(anno.type.tsym)).anyMatch(name -> name == names.RECORD_COMPONENT)
                     ).collect(List.collector()));
-                    rc.appendUniqueTypeAttributes(s.getRawTypeAttributes());
+                    rc.setTypeAttributes(s.getRawTypeAttributes());
                     // to get all the type annotations applied to the type
                     rc.type = s.type;
                 }
             }
         }
 
-        if (a.type.tsym.isAnnotationType() && !annotationApplicable(a, s)) {
-            if (isRecordMember && (s.flags_field & Flags.GENERATED_MEMBER) != 0) {
-                /* so we have found an annotation that is not applicable to a record member that was generated by the
-                 * compiler. This was intentionally done at TypeEnter, now is the moment strip away the annotations
-                 * that are not applicable to the given record member
-                 */
-                JCModifiers modifiers = TreeInfo.getModifiers(declarationTree);
-                // lets first remove the annotation from the modifier
-                if (modifiers != null) {
-                    ListBuffer<JCAnnotation> newAnnotations = new ListBuffer<>();
-                    for (JCAnnotation anno : modifiers.annotations) {
-                        if (anno != a) {
-                            newAnnotations.add(anno);
+        /* the section below is tricky. Annotations applied to record components are propagated to the corresponding
+         * record member so if an annotation has target: FIELD, it is propagated to the corresponding FIELD, if it has
+         * target METHOD, it is propagated to the accessor and so on. But at the moment when method members are generated
+         * there is no enough information to propagate only the right annotations. So all the annotations are propagated
+         * to all the possible locations.
+         *
+         * At this point we need to remove all the annotations that are not in place before going on with the annotation
+         * party. On top of the above there is the issue that there is no AST representing record components, just symbols
+         * so the corresponding field has been holding all the annotations and it's metadata has been modified as if it
+         * was both a field and a record component.
+         *
+         * So there are two places where we need to trim annotations from: the metadata of the symbol and / or the modifiers
+         * in the AST. Whatever is in the metadata will be written to the class file, whatever is in the modifiers could
+         * be see by annotation processors.
+         *
+         * The metadata contains both type annotations and declaration annotations. At this point of the game we don't
+         * need to care about type annotations, they are all in the right place. But we could need to remove declaration
+         * annotations. So for declaration annotations if they are not applicable to the record member, excluding type
+         * annotations which are already correct, then we will remove it. For the AST modifiers if the annotation is not
+         * applicable either as type annotation and or declaration annotation, only in that case it will be removed.
+         *
+         * So it could be that annotation is removed as a declaration annotation but it is kept in the AST modifier for
+         * further inspection by annotation processors.
+         *
+         * For example:
+         *
+         *     import java.lang.annotation.*;
+         *
+         *     @Target({ElementType.TYPE_USE, ElementType.RECORD_COMPONENT})
+         *     @Retention(RetentionPolicy.RUNTIME)
+         *     @interface Anno { }
+         *
+         *     record R(@Anno String s) {}
+         *
+         * at this point we will have for the case of the generated field:
+         *   - @Anno in the modifier
+         *   - @Anno as a type annotation
+         *   - @Anno as a declaration annotation
+         *
+         * the last one should be removed because the annotation has not FIELD as target but it was applied as a
+         * declaration annotation because the field was being treated both as a field and as a record component
+         * as we have already copied the annotations to the record component, now the field doesn't need to hold
+         * annotations that are not intended for it anymore. Still @Anno has to be kept in the AST's modifiers as it
+         * is applicable as a type annotation to the type of the field.
+         */
+
+        if (a.type.tsym.isAnnotationType()) {
+            Optional<Set<Name>> applicableTargetsOp = getApplicableTargets(a, s);
+            if (!applicableTargetsOp.isEmpty()) {
+                Set<Name> applicableTargets = applicableTargetsOp.get();
+                boolean notApplicableOrIsTypeUseOnly = applicableTargets.isEmpty() ||
+                        applicableTargets.size() == 1 && applicableTargets.contains(names.TYPE_USE);
+                boolean isRecordMemberWithNonApplicableDeclAnno =
+                        isRecordMember && (s.flags_field & Flags.GENERATED_MEMBER) != 0 && notApplicableOrIsTypeUseOnly;
+
+                if (applicableTargets.isEmpty() || isRecordMemberWithNonApplicableDeclAnno) {
+                    if (isRecordMemberWithNonApplicableDeclAnno) {
+                            /* so we have found an annotation that is not applicable to a record member that was generated by the
+                             * compiler. This was intentionally done at TypeEnter, now is the moment strip away the annotations
+                             * that are not applicable to the given record member
+                             */
+                        JCModifiers modifiers = TreeInfo.getModifiers(declarationTree);
+                            /* lets first remove the annotation from the modifier if it is not applicable, we have to check again as
+                             * it could be a type annotation
+                             */
+                        if (modifiers != null && applicableTargets.isEmpty()) {
+                            ListBuffer<JCAnnotation> newAnnotations = new ListBuffer<>();
+                            for (JCAnnotation anno : modifiers.annotations) {
+                                if (anno != a) {
+                                    newAnnotations.add(anno);
+                                }
+                            }
+                            modifiers.annotations = newAnnotations.toList();
                         }
+                        // now lets remove it from the symbol
+                        s.getMetadata().removeDeclarationMetadata(a.attribute);
+                    } else {
+                        log.error(a.pos(), Errors.AnnotationTypeNotApplicable);
                     }
-                    modifiers.annotations = newAnnotations.toList();
                 }
-                // now lets remove it from the symbol
-                s.getMetadata().remove(a.attribute);
-            } else {
-                log.error(a.pos(), Errors.AnnotationTypeNotApplicable);
             }
         }
 
@@ -3221,10 +3280,20 @@
         return targets;
     }
 
+    boolean annotationApplicable(JCAnnotation a, Symbol s) {
+        Optional<Set<Name>> targets = getApplicableTargets(a, s);
+        /* the optional could be emtpy if the annotation is unknown in that case
+         * we return that it is applicable and if it is erroneous that should imply
+         * an error at the declaration site
+         */
+        return targets.isEmpty() || targets.isPresent() && !targets.get().isEmpty();
+    }
+
     @SuppressWarnings("preview")
-    boolean annotationApplicable(JCAnnotation a, Symbol s) {
+    Optional<Set<Name>> getApplicableTargets(JCAnnotation a, Symbol s) {
         Attribute.Array arr = getAttributeTargetAttribute(a.annotationType.type.tsym);
         Name[] targets;
+        Set<Name> applicableTargets = new HashSet<>();
 
         if (arr == null) {
             targets = defaultTargetMetaInfo();
@@ -3234,7 +3303,8 @@
             for (int i=0; i<arr.values.length; ++i) {
                 Attribute app = arr.values[i];
                 if (!(app instanceof Attribute.Enum)) {
-                    return true; // recovery
+                    // recovery
+                    return Optional.empty();
                 }
                 Attribute.Enum e = (Attribute.Enum) app;
                 targets[i] = e.value.name;
@@ -3243,55 +3313,55 @@
         for (Name target : targets) {
             if (target == names.TYPE) {
                 if (s.kind == TYP)
-                    return true;
+                    applicableTargets.add(names.TYPE);
             } else if (target == names.FIELD) {
                 if (s.kind == VAR && s.owner.kind != MTH)
-                    return true;
+                    applicableTargets.add(names.FIELD);
             } else if (target == names.RECORD_COMPONENT) {
                 if (s.getKind() == ElementKind.RECORD_COMPONENT) {
-                    return true;
+                    applicableTargets.add(names.RECORD_COMPONENT);
                 }
             } else if (target == names.METHOD) {
                 if (s.kind == MTH && !s.isConstructor())
-                    return true;
+                    applicableTargets.add(names.METHOD);
             } else if (target == names.PARAMETER) {
                 if (s.kind == VAR &&
                     (s.owner.kind == MTH && (s.flags() & PARAMETER) != 0)) {
-                    return true;
+                    applicableTargets.add(names.PARAMETER);
                 }
             } else if (target == names.CONSTRUCTOR) {
                 if (s.kind == MTH && s.isConstructor())
-                    return true;
+                    applicableTargets.add(names.CONSTRUCTOR);
             } else if (target == names.LOCAL_VARIABLE) {
                 if (s.kind == VAR && s.owner.kind == MTH &&
                       (s.flags() & PARAMETER) == 0) {
-                    return true;
+                    applicableTargets.add(names.LOCAL_VARIABLE);
                 }
             } else if (target == names.ANNOTATION_TYPE) {
                 if (s.kind == TYP && (s.flags() & ANNOTATION) != 0) {
-                    return true;
+                    applicableTargets.add(names.ANNOTATION_TYPE);
                 }
             } else if (target == names.PACKAGE) {
                 if (s.kind == PCK)
-                    return true;
+                    applicableTargets.add(names.PACKAGE);
             } else if (target == names.TYPE_USE) {
                 if (s.kind == VAR && s.owner.kind == MTH && s.type.hasTag(NONE)) {
                     //cannot type annotate implicitly typed locals
-                    return false;
+                    continue;
                 } else if (s.kind == TYP || s.kind == VAR ||
                         (s.kind == MTH && !s.isConstructor() &&
                                 !s.type.getReturnType().hasTag(VOID)) ||
                         (s.kind == MTH && s.isConstructor())) {
-                    return true;
+                    applicableTargets.add(names.TYPE_USE);
                 }
             } else if (target == names.TYPE_PARAMETER) {
                 if (s.kind == TYP && s.type.hasTag(TYPEVAR))
-                    return true;
+                    applicableTargets.add(names.TYPE_PARAMETER);
             } else
-                return true; // Unknown ElementType. This should be an error at declaration site,
-                             // assume applicable.
+                return Optional.empty(); // Unknown ElementType. This should be an error at declaration site,
+                                         // assume applicable.
         }
-        return false;
+        return Optional.of(applicableTargets);
     }
 
     Attribute.Array getAttributeTargetAttribute(TypeSymbol s) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Thu Jan 23 15:52:54 2020 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Thu Jan 23 19:20:11 2020 -0500
@@ -918,7 +918,7 @@
                 List<JCVariableDecl> fields = TreeInfo.recordFields(tree);
                 memberEnter.memberEnter(fields, env);
                 for (JCVariableDecl field : fields) {
-                    sym.getRecordComponent(field.sym, true);
+                    sym.getRecordComponent(field, true);
                 }
 
                 enterThisAndSuper(sym, env);
@@ -1034,15 +1034,16 @@
 
         private void addAccessor(JCVariableDecl tree, Env<AttrContext> env) {
             MethodSymbol implSym = lookupMethod(env.enclClass.sym, tree.sym.name, List.nil());
-            RecordComponent rec = ((ClassSymbol) tree.sym.owner).getRecordComponent(tree.sym, false);
+            RecordComponent rec = ((ClassSymbol) tree.sym.owner).getRecordComponent(tree.sym);
             if (implSym == null || (implSym.flags_field & GENERATED_MEMBER) != 0) {
                 /* here we are pushing the annotations present in the corresponding field down to the accessor
                  * it could be that some of those annotations are not applicable to the accessor, they will be striped
                  * away later at Check::validateAnnotation
                  */
+                List<JCAnnotation> originalAnnos = rec.getOriginalAnnos();
                 JCMethodDecl getter = make.at(tree.pos).
                         MethodDef(
-                                make.Modifiers(Flags.PUBLIC | Flags.GENERATED_MEMBER, tree.mods.annotations),
+                                make.Modifiers(Flags.PUBLIC | Flags.GENERATED_MEMBER, originalAnnos),
                           tree.sym.name,
                           /* we need to special case for the case when the user declared the type as an ident
                            * if we don't do that then we can have issues if type annotations are applied to the
@@ -1349,7 +1350,8 @@
                 /* at this point we are passing all the annotations in the field to the corresponding
                  * parameter in the constructor.
                  */
-                arg.mods.annotations = tmpRecordFieldDecls.head.mods.annotations;
+                RecordComponent rc = ((ClassSymbol) owner).getRecordComponent(arg.sym);
+                arg.mods.annotations = rc.getOriginalAnnos();
                 arg.vartype = tmpRecordFieldDecls.head.vartype;
                 tmpRecordFieldDecls = tmpRecordFieldDecls.tail;
             }
--- a/test/langtools/tools/javac/records/RecordCompilationTests.java	Thu Jan 23 15:52:54 2020 -0800
+++ b/test/langtools/tools/javac/records/RecordCompilationTests.java	Thu Jan 23 19:20:11 2020 -0500
@@ -28,10 +28,12 @@
  *
  * @test
  * @summary Negative compilation tests, and positive compilation (smoke) tests for records
- * @library /lib/combo
+ * @library /lib/combo /tools/lib /tools/javac/lib
  * @modules
+ *      jdk.compiler/com.sun.tools.javac.code
  *      jdk.compiler/com.sun.tools.javac.util
  *      jdk.jdeps/com.sun.tools.classfile
+ * @build JavacTestingAbstractProcessor
  * @compile --enable-preview -source ${jdk.version} RecordCompilationTests.java
  * @run testng/othervm --enable-preview RecordCompilationTests
  */
@@ -39,17 +41,54 @@
 import java.io.File;
 
 import java.lang.annotation.ElementType;
+import java.util.Arrays;
 import java.util.EnumMap;
 import java.util.EnumSet;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+
 import com.sun.tools.javac.util.Assert;
 
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.RecordComponentElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.TypeMirror;
+
+import com.sun.tools.classfile.Annotation;
+import com.sun.tools.classfile.Attribute;
+import com.sun.tools.classfile.Attributes;
 import com.sun.tools.classfile.ClassFile;
 import com.sun.tools.classfile.ConstantPool;
 import com.sun.tools.classfile.ConstantPool.CPInfo;
+import com.sun.tools.classfile.Field;
+import com.sun.tools.classfile.Method;
+import com.sun.tools.classfile.Record_attribute;
+import com.sun.tools.classfile.Record_attribute.ComponentInfo;
+import com.sun.tools.classfile.RuntimeAnnotations_attribute;
+import com.sun.tools.classfile.RuntimeTypeAnnotations_attribute;
+import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute;
+import com.sun.tools.classfile.RuntimeVisibleParameterAnnotations_attribute;
+import com.sun.tools.classfile.RuntimeVisibleTypeAnnotations_attribute;
+import com.sun.tools.classfile.TypeAnnotation;
+
+import com.sun.tools.javac.code.Attribute.TypeCompound;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
 
 import org.testng.annotations.Test;
 import tools.javac.combo.CompilationTestCase;
@@ -585,4 +624,332 @@
         }
         Assert.check(numberOfFieldRefs == 1);
     }
+
+    public void testAnnos() throws Exception {
+        String srcTemplate =
+                """
+                    import java.lang.annotation.*;
+                    @Target({#TARGET})
+                    @Retention(RetentionPolicy.RUNTIME)
+                    @interface Anno { }
+
+                    record R(@Anno String s) {}
+                """;
+
+        // testing several combinations, adding even more combinations won't add too much value
+        List<String> annoApplicableTargets = List.of(
+                "ElementType.FIELD",
+                "ElementType.METHOD",
+                "ElementType.PARAMETER",
+                "ElementType.RECORD_COMPONENT",
+                "ElementType.TYPE_USE",
+                "ElementType.TYPE_USE,ElementType.FIELD",
+                "ElementType.TYPE_USE,ElementType.METHOD",
+                "ElementType.TYPE_USE,ElementType.PARAMETER",
+                "ElementType.TYPE_USE,ElementType.RECORD_COMPONENT",
+                "ElementType.TYPE_USE,ElementType.FIELD,ElementType.METHOD",
+                "ElementType.TYPE_USE,ElementType.FIELD,ElementType.PARAMETER",
+                "ElementType.TYPE_USE,ElementType.FIELD,ElementType.RECORD_COMPONENT",
+                "ElementType.FIELD,ElementType.TYPE_USE",
+                "ElementType.METHOD,ElementType.TYPE_USE",
+                "ElementType.PARAMETER,ElementType.TYPE_USE",
+                "ElementType.RECORD_COMPONENT,ElementType.TYPE_USE",
+                "ElementType.FIELD,ElementType.METHOD,ElementType.TYPE_USE",
+                "ElementType.FIELD,ElementType.PARAMETER,ElementType.TYPE_USE",
+                "ElementType.FIELD,ElementType.RECORD_COMPONENT,ElementType.TYPE_USE"
+        );
+
+        String[] generalOptions = {
+                "--enable-preview",
+                "-source", Integer.toString(Runtime.version().feature()),
+                "-processor", Processor.class.getName(),
+                "-Atargets="
+        };
+
+        for (String target : annoApplicableTargets) {
+            String code = srcTemplate.replaceFirst("#TARGET", target);
+            String[] testOptions = generalOptions.clone();
+            testOptions[testOptions.length - 1] = testOptions[testOptions.length - 1] + target;
+            setCompileOptions(testOptions);
+
+            File dir = assertOK(true, code);
+
+            ClassFile classFile = ClassFile.read(findClassFileOrFail(dir, "R.class"));
+
+            // field first
+            Assert.check(classFile.fields.length == 1);
+            Field field = classFile.fields[0];
+            /* if FIELD is one of the targets then there must be a declaration annotation applied to the field, apart from
+             * the type annotation
+             */
+            if (target.contains("FIELD")) {
+                checkAnno(classFile,
+                        (RuntimeAnnotations_attribute)findAttributeOrFail(
+                                field.attributes,
+                                RuntimeVisibleAnnotations_attribute.class),
+                        "Anno");
+            } else {
+                assertAttributeNotPresent(field.attributes, RuntimeVisibleAnnotations_attribute.class);
+            }
+
+            // lets check now for the type annotation
+            if (target.contains("TYPE_USE")) {
+                checkTypeAnno(
+                        classFile,
+                        (RuntimeVisibleTypeAnnotations_attribute)findAttributeOrFail(field.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
+                        "FIELD",
+                        "Anno");
+            } else {
+                assertAttributeNotPresent(field.attributes, RuntimeVisibleTypeAnnotations_attribute.class);
+            }
+
+            // checking for the annotation on the corresponding parameter of the canonical constructor
+            Method init = findMethodOrFail(classFile, "<init>");
+            /* if PARAMETER is one of the targets then there must be a declaration annotation applied to the parameter, apart from
+             * the type annotation
+             */
+            if (target.contains("PARAMETER")) {
+                checkParameterAnno(classFile,
+                        (RuntimeVisibleParameterAnnotations_attribute)findAttributeOrFail(
+                                init.attributes,
+                                RuntimeVisibleParameterAnnotations_attribute.class),
+                        "Anno");
+            } else {
+                assertAttributeNotPresent(init.attributes, RuntimeVisibleAnnotations_attribute.class);
+            }
+            // let's check now for the type annotation
+            if (target.contains("TYPE_USE")) {
+                checkTypeAnno(
+                        classFile,
+                        (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(init.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
+                        "METHOD_FORMAL_PARAMETER", "Anno");
+            } else {
+                assertAttributeNotPresent(init.attributes, RuntimeVisibleTypeAnnotations_attribute.class);
+            }
+
+            // checking for the annotation in the accessor
+            Method accessor = findMethodOrFail(classFile, "s");
+            /* if METHOD is one of the targets then there must be a declaration annotation applied to the accessor, apart from
+             * the type annotation
+             */
+            if (target.contains("METHOD")) {
+                checkAnno(classFile,
+                        (RuntimeAnnotations_attribute)findAttributeOrFail(
+                                accessor.attributes,
+                                RuntimeVisibleAnnotations_attribute.class),
+                        "Anno");
+            } else {
+                assertAttributeNotPresent(accessor.attributes, RuntimeVisibleAnnotations_attribute.class);
+            }
+            // let's check now for the type annotation
+            if (target.contains("TYPE_USE")) {
+                checkTypeAnno(
+                        classFile,
+                        (RuntimeVisibleTypeAnnotations_attribute)findAttributeOrFail(accessor.attributes, RuntimeVisibleTypeAnnotations_attribute.class),
+                        "METHOD_RETURN", "Anno");
+            } else {
+                assertAttributeNotPresent(accessor.attributes, RuntimeVisibleTypeAnnotations_attribute.class);
+            }
+
+            // checking for the annotation in the Record attribute
+            Record_attribute record = (Record_attribute)findAttributeOrFail(classFile.attributes, Record_attribute.class);
+            Assert.check(record.component_count == 1);
+            /* if RECORD_COMPONENT is one of the targets then there must be a declaration annotation applied to the
+             * field, apart from the type annotation
+             */
+            if (target.contains("RECORD_COMPONENT")) {
+                checkAnno(classFile,
+                        (RuntimeAnnotations_attribute)findAttributeOrFail(
+                                record.component_info_arr[0].attributes,
+                                RuntimeVisibleAnnotations_attribute.class),
+                        "Anno");
+            } else {
+                assertAttributeNotPresent(record.component_info_arr[0].attributes, RuntimeVisibleAnnotations_attribute.class);
+            }
+            // lets check now for the type annotation
+            if (target.contains("TYPE_USE")) {
+                checkTypeAnno(
+                        classFile,
+                        (RuntimeVisibleTypeAnnotations_attribute)findAttributeOrFail(
+                                record.component_info_arr[0].attributes,
+                                RuntimeVisibleTypeAnnotations_attribute.class),
+                        "FIELD", "Anno");
+            } else {
+                assertAttributeNotPresent(record.component_info_arr[0].attributes, RuntimeVisibleTypeAnnotations_attribute.class);
+            }
+        }
+
+        // let's reset the default compiler options for other tests
+        setCompileOptions(PREVIEW_OPTIONS);
+    }
+
+    private void checkTypeAnno(ClassFile classFile,
+                               RuntimeTypeAnnotations_attribute rtAnnos,
+                               String positionType,
+                               String annoName) throws Exception {
+        // containing only one type annotation
+        Assert.check(rtAnnos.annotations.length == 1);
+        TypeAnnotation tAnno = (TypeAnnotation)rtAnnos.annotations[0];
+        Assert.check(tAnno.position.type.toString().equals(positionType));
+        String annotationName = classFile.constant_pool.getUTF8Value(tAnno.annotation.type_index).toString().substring(1);
+        Assert.check(annotationName.startsWith(annoName));
+    }
+
+    private void checkAnno(ClassFile classFile,
+                           RuntimeAnnotations_attribute rAnnos,
+                           String annoName) throws Exception {
+        // containing only one type annotation
+        Assert.check(rAnnos.annotations.length == 1);
+        Annotation anno = (Annotation)rAnnos.annotations[0];
+        String annotationName = classFile.constant_pool.getUTF8Value(anno.type_index).toString().substring(1);
+        Assert.check(annotationName.startsWith(annoName));
+    }
+
+    // special case for parameter annotations
+    private void checkParameterAnno(ClassFile classFile,
+                           RuntimeVisibleParameterAnnotations_attribute rAnnos,
+                           String annoName) throws Exception {
+        // containing only one type annotation
+        Assert.check(rAnnos.parameter_annotations.length == 1);
+        Assert.check(rAnnos.parameter_annotations[0].length == 1);
+        Annotation anno = (Annotation)rAnnos.parameter_annotations[0][0];
+        String annotationName = classFile.constant_pool.getUTF8Value(anno.type_index).toString().substring(1);
+        Assert.check(annotationName.startsWith(annoName));
+    }
+
+    private File findClassFileOrFail(File dir, String name) {
+        for (final File fileEntry : dir.listFiles()) {
+            if (fileEntry.getName().equals("R.class")) {
+                return fileEntry;
+            }
+        }
+        throw new AssertionError("file not found");
+    }
+
+    private Method findMethodOrFail(ClassFile classFile, String name) throws Exception {
+        for (Method method : classFile.methods) {
+            if (method.getName(classFile.constant_pool).equals(name)) {
+                return method;
+            }
+        }
+        throw new AssertionError("method not found");
+    }
+
+    private Attribute findAttributeOrFail(Attributes attributes, Class<? extends Attribute> attrClass) {
+        for (Attribute attribute : attributes) {
+            if (attribute.getClass() == attrClass) {
+                return attribute;
+            }
+        }
+        throw new AssertionError("attribute not found");
+    }
+
+    private void assertAttributeNotPresent(Attributes attributes, Class<? extends Attribute> attrClass) {
+        for (Attribute attribute : attributes) {
+            if (attribute.getClass() == attrClass) {
+                throw new AssertionError("attribute not expected");
+            }
+        }
+    }
+
+    @SupportedAnnotationTypes("*")
+    public static final class Processor extends JavacTestingAbstractProcessor {
+
+        String targets;
+        int numberOfTypeAnnotations;
+
+        @Override
+        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+            targets = processingEnv.getOptions().get("targets");
+            System.out.println("targets------------------------------------------------- " + targets);
+            for (TypeElement te : annotations) {
+                if (te.toString().equals("Anno")) {
+                    checkElements(te, roundEnv, targets);
+                    if (targets.contains("TYPE_USE")) {
+                        Element element = processingEnv.getElementUtils().getTypeElement("R");
+                        numberOfTypeAnnotations = 0;
+                        System.out.println("element found --------------------------------- " + element);
+                        checkTypeAnnotations(element);
+                        Assert.check(numberOfTypeAnnotations == 4);
+                    }
+                }
+            }
+            return true;
+        }
+
+        void checkElements(TypeElement te, RoundEnvironment renv, String targets) {
+            Set<? extends Element> annoElements = renv.getElementsAnnotatedWith(te);
+            Set<String> targetSet = new HashSet<>(Arrays.asList(targets.split(",")));
+            // we will check for type annotation in another method
+            targetSet.remove("ElementType.TYPE_USE");
+            for (Element e : annoElements) {
+                Symbol s = (Symbol) e;
+                switch (s.getKind()) {
+                    case FIELD:
+                        Assert.check(targetSet.contains("ElementType.FIELD"));
+                        targetSet.remove("ElementType.FIELD");
+                        break;
+                    case METHOD:
+                        Assert.check(targetSet.contains("ElementType.METHOD"));
+                        targetSet.remove("ElementType.METHOD");
+                        break;
+                    case PARAMETER:
+                        Assert.check(targetSet.contains("ElementType.PARAMETER"));
+                        targetSet.remove("ElementType.PARAMETER");
+                        break;
+                    case RECORD_COMPONENT:
+                        Assert.check(targetSet.contains("ElementType.RECORD_COMPONENT"));
+                        targetSet.remove("ElementType.RECORD_COMPONENT");
+                        break;
+                    default:
+                        throw new AssertionError("unexpected element kind");
+                }
+            }
+            Assert.check(targetSet.isEmpty(), targetSet.toString());
+        }
+
+        private void checkTypeAnnotations(Element rootElement) {
+            new ElementScanner<Void, Void>() {
+                @Override public Void visitVariable(VariableElement e, Void p) {
+                    Symbol s = (Symbol) e;
+                    if (s.getKind() == ElementKind.FIELD ||
+                            s.getKind() == ElementKind.PARAMETER &&
+                            s.name.toString().equals("s")) {
+                        int currentTAs = numberOfTypeAnnotations;
+                        verifyTypeAnnotations(e.asType().getAnnotationMirrors());
+                        Assert.check(currentTAs + 1 == numberOfTypeAnnotations);
+                    }
+                    return null;
+                }
+                @Override
+                public Void visitExecutable(ExecutableElement e, Void p) {
+                    Symbol s = (Symbol) e;
+                    if (s.getKind() == ElementKind.METHOD &&
+                                    s.name.toString().equals("s")) {
+                        int currentTAs = numberOfTypeAnnotations;
+                        verifyTypeAnnotations(e.getReturnType().getAnnotationMirrors());
+                        Assert.check(currentTAs + 1 == numberOfTypeAnnotations);
+                    }
+                    scan(e.getParameters(), p);
+                    return null;
+                }
+                @Override public Void visitRecordComponent(RecordComponentElement e, Void p) {
+                    int currentTAs = numberOfTypeAnnotations;
+                    verifyTypeAnnotations(e.asType().getAnnotationMirrors());
+                    Assert.check(currentTAs + 1 == numberOfTypeAnnotations);
+                    return null;
+                }
+            }.scan(rootElement, null);
+        }
+
+        private void verifyTypeAnnotations(Iterable<? extends AnnotationMirror> annotations) {
+            for (AnnotationMirror mirror : annotations) {
+                Assert.check(mirror.toString().startsWith("@Anno"));
+                if (mirror instanceof TypeCompound) {
+                    numberOfTypeAnnotations++;
+                }
+            }
+        }
+
+    }
 }