8011027: Type parameter annotations not passed through to javax.lang.model
authorjfranck
Mon, 22 Apr 2013 10:24:19 +0200
changeset 17278 a48ec76f26e9
parent 17277 f69990e2fdbd
child 17279 c5355494c1b3
child 18396 aff9a5d60824
8011027: Type parameter annotations not passed through to javax.lang.model Reviewed-by: jjg, darcy
langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java
langtools/src/share/classes/com/sun/tools/javac/code/TypeAnnotations.java
langtools/src/share/classes/com/sun/tools/javac/model/JavacAnnoConstructs.java
langtools/src/share/classes/com/sun/tools/javac/model/JavacElements.java
langtools/test/tools/javac/processing/model/element/TestTypeParameterAnnotations.java
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java	Fri Apr 19 11:10:40 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java	Mon Apr 22 10:24:19 2013 +0200
@@ -465,19 +465,12 @@
      * This is the implementation for {@code
      * javax.lang.model.element.Element.getAnnotationMirrors()}.
      */
-    public final List<? extends AnnotationMirror> getAnnotationMirrors() {
+    @Override
+    public List<Attribute.Compound> getAnnotationMirrors() {
         return getRawAttributes();
     }
 
     /**
-     * TODO: Should there be a {@code
-     * javax.lang.model.element.Element.getTypeAnnotationMirrors()}.
-     */
-    public final List<Attribute.TypeCompound> getTypeAnnotationMirrors() {
-        return getRawTypeAttributes();
-    }
-
-    /**
      * @deprecated this method should never be used by javac internally.
      */
     @Deprecated
@@ -657,6 +650,24 @@
         }
 
         @Override
+        public List<Attribute.Compound> getAnnotationMirrors() {
+            return onlyTypeVariableAnnotations(owner.getRawTypeAttributes());
+        }
+
+        private List<Attribute.Compound> onlyTypeVariableAnnotations(
+                List<Attribute.TypeCompound> candidates) {
+            // Declaration annotations on TypeParameters are stored in type attributes
+            List<Attribute.Compound> res = List.nil();
+            for (Attribute.TypeCompound a : candidates) {
+                if (a.position.type == TargetType.CLASS_TYPE_PARAMETER ||
+                    a.position.type == TargetType.METHOD_TYPE_PARAMETER)
+                    res = res.prepend(a);
+            }
+
+            return res = res.reverse();
+        }
+
+        @Override
         public <R, P> R accept(ElementVisitor<R, P> v, P p) {
             return v.visitTypeParameter(this, p);
         }
--- a/langtools/src/share/classes/com/sun/tools/javac/code/TypeAnnotations.java	Fri Apr 19 11:10:40 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/TypeAnnotations.java	Mon Apr 22 10:24:19 2013 +0200
@@ -206,7 +206,7 @@
                     sym.getKind() == ElementKind.EXCEPTION_PARAMETER) {
                 // Make sure all type annotations from the symbol are also
                 // on the owner.
-                sym.owner.annotations.appendUniqueTypes(sym.getTypeAnnotationMirrors());
+                sym.owner.annotations.appendUniqueTypes(sym.getRawTypeAttributes());
             }
         }
 
--- a/langtools/src/share/classes/com/sun/tools/javac/model/JavacAnnoConstructs.java	Fri Apr 19 11:10:40 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/model/JavacAnnoConstructs.java	Mon Apr 22 10:24:19 2013 +0200
@@ -33,10 +33,13 @@
 import com.sun.tools.javac.code.Kinds;
 import com.sun.tools.javac.code.Symbol;
 import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.TypeVariableSymbol;
+import com.sun.tools.javac.code.TargetType;
 import com.sun.tools.javac.code.Type;
 import com.sun.tools.javac.code.Type.AnnotatedType;
 import com.sun.tools.javac.util.ListBuffer;
 import static com.sun.tools.javac.code.TypeTag.CLASS;
+import com.sun.tools.javac.util.List;
 
 /**
  * Utility methods for operating on annotated constructs.
@@ -61,8 +64,12 @@
             throw new IllegalArgumentException("Not an annotation type: "
                                                + annoType);
         Attribute.Compound c;
-        if (annotated.kind == Kinds.TYP && annotated instanceof ClassSymbol) {
+        if (annotated.kind == Kinds.TYP &&
+                annotated instanceof ClassSymbol) {
             c = getAttributeOnClass((ClassSymbol)annotated, annoType);
+        } else if (annotated.kind == Kinds.TYP &&
+                   annotated instanceof TypeVariableSymbol) {
+            c = getAttributeOnTypeVariable((TypeVariableSymbol)annotated, annoType);
         } else {
             c = getAttribute(annotated, annoType);
         }
@@ -83,6 +90,24 @@
     }
 
     // Helper to getAnnotation[s]
+    private static <A extends Annotation> Attribute.Compound
+            getAttributeOnTypeVariable(TypeVariableSymbol annotated, Class<A> annoType) {
+        String name = annoType.getName();
+
+        // Declaration annotations on type variables are stored in type attributes
+        // on the owner of the TypeVariableSymbol
+        List<Attribute.Compound> res = List.nil();
+        List<Attribute.TypeCompound> candidates = annotated.owner.getRawTypeAttributes();
+        for (Attribute.TypeCompound anno : candidates)
+            if (anno.position.type == TargetType.CLASS_TYPE_PARAMETER ||
+                    anno.position.type == TargetType.METHOD_TYPE_PARAMETER)
+                if (name.equals(anno.type.tsym.flatName().toString()))
+                    return anno;
+
+        return null;
+    }
+
+    // Helper to getAnnotation[s]
     private static <A extends Annotation> Attribute.Compound getAttributeOnClass(ClassSymbol annotated,
                                                                 Class<A> annoType) {
         boolean inherited = annoType.isAnnotationPresent(Inherited.class);
--- a/langtools/src/share/classes/com/sun/tools/javac/model/JavacElements.java	Fri Apr 19 11:10:40 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/model/JavacElements.java	Mon Apr 22 10:24:19 2013 +0200
@@ -402,9 +402,10 @@
      * @param e  the element being examined
      * @return all annotations of the element
      */
+    @Override
     public List<Attribute.Compound> getAllAnnotationMirrors(Element e) {
         Symbol sym = cast(Symbol.class, e);
-        List<Attribute.Compound> annos = sym.getRawAttributes();
+        List<Attribute.Compound> annos = sym.getAnnotationMirrors();
         while (sym.getKind() == ElementKind.CLASS) {
             Type sup = ((ClassSymbol) sym).getSuperclass();
             if (!sup.hasTag(CLASS) || sup.isErroneous() ||
@@ -413,7 +414,7 @@
             }
             sym = sup.tsym;
             List<Attribute.Compound> oldAnnos = annos;
-            List<Attribute.Compound> newAnnos = sym.getRawAttributes();
+            List<Attribute.Compound> newAnnos = sym.getAnnotationMirrors();
             for (Attribute.Compound anno : newAnnos) {
                 if (isInherited(anno.type) &&
                         !containsAnnoOfType(oldAnnos, anno.type)) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/model/element/TestTypeParameterAnnotations.java	Mon Apr 22 10:24:19 2013 +0200
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2013, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8011027
+ * @library /tools/javac/lib
+ * @build JavacTestingAbstractProcessor TestTypeParameterAnnotations
+ * @compile -processor TestTypeParameterAnnotations -proc:only TestTypeParameterAnnotations.java
+ */
+
+import java.util.*;
+import java.lang.annotation.*;
+import javax.annotation.processing.*;
+import javax.lang.model.element.*;
+import javax.lang.model.util.*;
+import javax.tools.*;
+
+public class TestTypeParameterAnnotations<@Foo @Bar @Baz T> extends JavacTestingAbstractProcessor {
+    int round = 0;
+
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        if (++round == 1) {
+            int found = (new Scanner()).scan(roundEnv.getRootElements(), null);
+            if (found == expect) {
+                ; //nop
+            } else {
+                error("unexpected number of results: expected " + expect
+                        + ", found " + found);
+            }
+
+        }
+        return true;
+    }
+
+    class Scanner extends JavacTestingAbstractProcessor.ElementScanner<Integer,Void> {
+        @Override
+        public Integer visitExecutable(ExecutableElement e, Void p) {
+            super.visitExecutable(e, p);
+            found += check(e, e.getTypeParameters());
+            return found;
+        }
+
+        @Override
+        public Integer visitType(TypeElement e, Void p) {
+            super.visitType(e, p);
+            found += check(e, e.getTypeParameters());
+            return found;
+        }
+
+        int found;
+    }
+
+    int check(Element e, List<? extends TypeParameterElement> typarams) {
+        if (typarams.isEmpty())
+            return 0;
+        if (typarams.size() != 1)
+            return 0;
+
+        for (TypeParameterElement tpe: typarams) {
+            boolean b1 = checkAnnotationMirrors(tpe, tpe.getAnnotationMirrors());
+            boolean b2 = checkAnnotationMirrors(tpe, elements.getAllAnnotationMirrors(tpe));
+            boolean b3 = checkGetAnnotation(tpe);
+            boolean b4 = checkGetAnnotations(tpe);
+            return b1 && b2 && b3 && b4 ? 1 : 0;
+        }
+        return 0;
+    }
+
+    boolean checkAnnotationMirrors(TypeParameterElement tpe, List<? extends AnnotationMirror> l) {
+        if (l.size() != 3) {
+            error("To few annotations, got " + l.size() +
+                    ", should be 3", tpe);
+            return false;
+        }
+
+        AnnotationMirror m = l.get(0);
+        if (!m.getAnnotationType().asElement().equals(elements.getTypeElement("Foo"))) {
+            error("Wrong type of annotation, was expecting @Foo", m.getAnnotationType().asElement());
+            return false;
+        }
+        m = l.get(1);
+        if (!m.getAnnotationType().asElement().equals(elements.getTypeElement("Bar"))) {
+            error("Wrong type of annotation, was expecting @Bar", m.getAnnotationType().asElement());
+            return false;
+        }
+        m = l.get(2);
+        if (!m.getAnnotationType().asElement().equals(elements.getTypeElement("Baz"))) {
+            error("Wrong type of annotation, was expecting @Baz", m.getAnnotationType().asElement());
+            return false;
+        }
+        return true;
+    }
+
+    boolean checkGetAnnotation(TypeParameterElement tpe) {
+        Foo f = tpe.getAnnotation(Foo.class);
+        if (f == null)
+            error("Expecting @Foo to be present in getAnnotation()", tpe);
+
+        Bar b = tpe.getAnnotation(Bar.class);
+        if (b == null)
+            error("Expecting @Bar to be present in getAnnotation()", tpe);
+
+        Baz z = tpe.getAnnotation(Baz.class);
+        if (z == null)
+            error("Expecting @Baz to be present in getAnnotation()", tpe);
+
+        return f != null &&
+            b != null &&
+            z != null;
+    }
+
+    boolean checkGetAnnotations(TypeParameterElement tpe) {
+        Foo[] f = tpe.getAnnotationsByType(Foo.class);
+        if (f.length != 1) {
+            error("Expecting 1 @Foo to be present in getAnnotationsByType()", tpe);
+            return false;
+        }
+
+        Bar[] b = tpe.getAnnotationsByType(Bar.class);
+        if (b.length != 1) {
+            error("Expecting 1 @Bar to be present in getAnnotationsByType()", tpe);
+            return false;
+        }
+
+        Baz[] z = tpe.getAnnotationsByType(Baz.class);
+        if (z.length != 1) {
+            error("Expecting 1 @Baz to be present in getAnnotationsByType()", tpe);
+            return false;
+        }
+
+        return true;
+    }
+
+    void note(String msg) {
+        messager.printMessage(Diagnostic.Kind.NOTE, msg);
+    }
+
+    void note(String msg, Element e) {
+        messager.printMessage(Diagnostic.Kind.NOTE, msg, e);
+    }
+
+    void error(String msg, Element e) {
+        messager.printMessage(Diagnostic.Kind.ERROR, msg, e);
+    }
+
+    void error(String msg) {
+        messager.printMessage(Diagnostic.Kind.ERROR, msg);
+    }
+
+    // additional generic elements to test
+    <@Foo @Bar @Baz X> X m(X x) { return x; }
+
+    interface Intf<@Foo @Bar @Baz X> { X m() ; }
+
+    class Class<@Foo @Bar @Baz X> {
+        <@Foo @Bar @Baz Y> Class() { }
+    }
+
+    final int expect = 5;  // top level class, plus preceding examples
+}
+
+@Target(ElementType.TYPE_PARAMETER)
+@interface Foo {}
+
+@Target(ElementType.TYPE_PARAMETER)
+@interface Bar {}
+
+@Target(ElementType.TYPE_PARAMETER)
+@interface Baz {}