langtools/test/tools/javac/processing/model/type/BasicAnnoTests.java
changeset 29842 826ac2519523
parent 26532 aa84b6606229
child 30730 d3ce7619db2c
--- a/langtools/test/tools/javac/processing/model/type/BasicAnnoTests.java	Fri Apr 03 16:35:58 2015 -0700
+++ b/langtools/test/tools/javac/processing/model/type/BasicAnnoTests.java	Tue Apr 07 11:04:29 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2015, 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
@@ -23,21 +23,27 @@
 
 /*
  * @test
- * @bug     8013852
+ * @bug 8013852 8031744
  * @summary Annotations on types
  * @library /tools/javac/lib
- * @ignore 8057688 type annotations in type argument position are lost
- * @ignore 8031744 Annotations on many Language Model elements are not returned
  * @build JavacTestingAbstractProcessor DPrinter BasicAnnoTests
  * @compile/process -processor BasicAnnoTests -proc:only BasicAnnoTests.java
  */
 
 import java.io.PrintWriter;
+import java.io.Serializable;
 import java.lang.annotation.Annotation;
 import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
 import java.lang.annotation.Target;
+import java.util.ArrayList;
+
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.NavigableMap;
 import java.util.Set;
+import java.util.TreeMap;
 
 import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
@@ -48,15 +54,23 @@
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
 import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.IntersectionType;
 import javax.lang.model.type.TypeMirror;
 import javax.lang.model.type.TypeVariable;
 import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.Types;
 import javax.tools.Diagnostic.Kind;
 
+import com.sun.tools.javac.code.Attribute;
 import com.sun.tools.javac.code.Symbol;
-import com.sun.tools.javac.code.Type;
 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
+import com.sun.tools.javac.util.Name;
+
+import static com.sun.tools.javac.code.Attribute.Array;
+import static com.sun.tools.javac.code.Attribute.Constant;
+import static com.sun.tools.javac.code.Attribute.Compound;
 
 /**
  * The test scans this file looking for test cases annotated with @Test.
@@ -77,7 +91,7 @@
     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
         TestElementScanner s = new TestElementScanner();
         for (Element e: roundEnv.getRootElements()) {
-            s.scan(e);
+            s.scan(e, null);
         }
         return true;
     }
@@ -95,17 +109,17 @@
      */
     class TestElementScanner extends ElementScanner<Void,Void> {
         public Void scan(Element elem, Void ignore) {
+            List<AnnotationMirror> tests = new ArrayList<>();
             AnnotationMirror test = getAnnotation(elem, Test.class.getName().replace('$', '.'));
             if (test != null) {
+                tests.add(test);
+            }
+            tests.addAll(getAnnotations(elem, Tests.class.getName().replace('$', '.')));
+
+            if (tests.size() > 0) {
                 out.println("Test: " + elem + " " + test);
-                TestTypeScanner s = new TestTypeScanner(elem, test);
-                s.scan(elem.asType(), null);
-                if (getPosn(test) >= s.count)
-                    error(elem, "position " + getPosn(test) + " not found");
-                if (!s.found) {
-                    dprinter.printSymbol("element", (Symbol) elem);
-                    dprinter.printType("type", (Type) elem.asType());
-                }
+                TestTypeScanner s = new TestTypeScanner(elem, tests, types);
+                s.test(elem.asType());
                 out.println();
             }
             return super.scan(elem, ignore);
@@ -118,45 +132,110 @@
      */
     class TestTypeScanner extends TypeScanner<Void, Void> {
         Element elem;
-        AnnotationMirror test;
+        NavigableMap<Integer, AnnotationMirror> toBeFound;
         int count = 0;
-        boolean found = false;
+        Set<TypeMirror> seen = new HashSet<>();
+
+        TestTypeScanner(Element elem, List<AnnotationMirror> tests, Types types) {
+            super(types);
+            this.elem = elem;
 
-        TestTypeScanner(Element elem, AnnotationMirror test) {
-            this.elem = elem;
-            this.test = test;
+            NavigableMap<Integer, AnnotationMirror> testByPos = new TreeMap<>();
+            for (AnnotationMirror test : tests) {
+                for (int pos : getPosn(test)) {
+                    testByPos.put(pos, test);
+                }
+            }
+            this.toBeFound = testByPos;
+        }
+
+        public void test(TypeMirror t) {
+            scan(t, null);
         }
 
         @Override
         Void scan(TypeMirror t, Void ignore) {
             if (t == null)
                 return DEFAULT_VALUE;
-            if (verbose)
-                out.println("scan " + count + ": " + t);
-            if (count == getPosn(test)) {
-                String annoType = getAnnoType(test);
-                AnnotationMirror anno = getAnnotation(t, annoType);
-                if (anno == null) {
-                    error(elem, "annotation not found on " + count + ": " + t);
-                } else {
-                    String v = getValue(anno, "value").toString();
-                    if (v.equals(getExpect(test))) {
-                        out.println("found " + anno + " as expected");
-                        found = true;
+
+            if (!seen.contains(t)) {
+                try {
+                    seen.add(t);
+                    if (verbose)
+                        out.println("scan " + count + ": " + t);
+                    if (toBeFound.size() > 0) {
+                        if (toBeFound.firstKey().equals(count)) {
+                            AnnotationMirror test = toBeFound.pollFirstEntry().getValue();
+                            String annoType = getAnnoType(test);
+                            AnnotationMirror anno = getAnnotation(t, annoType);
+                            if (anno == null) {
+                                error(elem, "annotation not found on " + count + ": " + t);
+                            } else {
+                                String v = getValue(anno, "value").toString();
+                                if (v.equals(getExpect(test))) {
+                                    out.println("found " + anno + " as expected");
+                                } else {
+                                    error(elem, "Unexpected value: " + v + ", expected: " + getExpect(test));
+                                }
+                            }
+                        } else if (count > toBeFound.firstKey()) {
+                            rescue();
+                        } else {
+                            List<? extends AnnotationMirror> annos = t.getAnnotationMirrors();
+                            if (annos.size() > 0) {
+                                for (AnnotationMirror a : annos)
+                                    error(elem, "annotation " + a + " found on " + count + ": " + t);
+                            }
+                        }
                     } else {
-                        error(elem, "Unexpected value: " + v + ", expected: " + getExpect(test));
+                        List<? extends AnnotationMirror> annos = t.getAnnotationMirrors();
+                        if (annos.size() > 0) {
+                            for (AnnotationMirror a : annos)
+                                error(elem, "annotation " + a + " found on " + count + ": " + t);
+                        }
                     }
+                    count++;
+                    return super.scan(t, ignore);
+
+                } finally {
+                    seen.remove(t);
                 }
             }
-            count++;
-            return super.scan(t, ignore);
+
+            return DEFAULT_VALUE;
+
+        }
+
+        private void rescue() {
+            while (toBeFound.size() > 0 && toBeFound.firstKey() >= count)
+                toBeFound.pollFirstEntry();
         }
     }
 
-    /** Get the position value from an @Test annotation mirror. */
-    static int getPosn(AnnotationMirror test) {
+    /** Get the position value from an element annotated with a @Test annotation mirror. */
+    static int[] getPosn(Element elem) {
+        return elem.getAnnotation(Test.class).posn();
+    }
+
+    /** Get the position value from a @Test annotation mirror. */
+    static Integer[] getPosn(AnnotationMirror test) {
         AnnotationValue v = getValue(test, "posn");
-        return (Integer) v.getValue();
+        Object value = v.getValue();
+        Integer i = 0;
+        if (value instanceof Constant) {
+            i = (Integer)((Constant)value).getValue();
+            Integer[] res = new Integer[1];
+            res[0] = i;
+            return res;
+        } else if (value instanceof List) {
+            List<Constant> l = (List<Constant>)value;
+            Integer[] res = new Integer[l.size()];
+            for (int c = 0; c < l.size(); c++) {
+                res[c] = (Integer)l.get(c).getValue();
+            }
+            return res;
+        }
+        return null;
     }
 
     /** Get the expect value from an @Test annotation mirror. */
@@ -185,6 +264,25 @@
         return null;
     }
 
+    static List<AnnotationMirror> getAnnotations(Element e, String name) {
+        Name valueName = ((Symbol)e).getSimpleName().table.names.value;
+        List<AnnotationMirror> res = new ArrayList<>();
+
+        for (AnnotationMirror m : e.getAnnotationMirrors()) {
+            TypeElement te = (TypeElement) m.getAnnotationType().asElement();
+            if (te.getQualifiedName().contentEquals(name)) {
+                Compound theAnno = (Compound)m;
+                Array valueArray = (Array)theAnno.member(valueName);
+                for (Attribute a : valueArray.getValue()) {
+                    AnnotationMirror theMirror = (AnnotationMirror) a;
+
+                    res.add(theMirror);
+                }
+            }
+        }
+        return res;
+    }
+
     /**
      * Get a specific value from an annotation mirror.
      */
@@ -203,6 +301,13 @@
      * one sufficient for our needs.
      */
     static class TypeScanner<R, P> extends SimpleTypeVisitor<R, P> {
+        private Types types;
+
+        public TypeScanner(Types types) {
+            super();
+            this.types = types;
+        }
+
         @Override
         public R visitArray(ArrayType t, P p) {
             scan(t.getComponentType(), p);
@@ -211,17 +316,34 @@
 
         @Override
         public R visitExecutable(ExecutableType t, P p) {
+            //out.println("  type parameters: " + t.getTypeVariables());
+            scan(t.getTypeVariables(), p);
+            //out.println("  return: " + t.getReturnType());
+            scan(t.getReturnType(), p);
+            //out.println("  receiver: " + t.getReceiverTypes());
             scan(t.getReceiverType());
             //out.println("  params: " + t.getParameterTypes());
             scan(t.getParameterTypes(), p);
-            //out.println("  return: " + t.getReturnType());
-            scan(t.getReturnType(), p);
             //out.println("  throws: " + t.getThrownTypes());
             scan(t.getThrownTypes(), p);
             return super.visitExecutable(t, p);
         }
 
         @Override
+        public R visitDeclared(DeclaredType t, P p) {
+            scan(t.getTypeArguments(), p);
+            // don't scan enclosing
+            scan(types.directSupertypes(t), p);
+            return super.visitDeclared(t, p);
+        }
+
+        @Override
+        public R visitIntersection(IntersectionType t, P p) {
+            scan(t.getBounds(), p);
+            return super.visitIntersection(t, p);
+        }
+
+        @Override
         public R visitTypeVariable(TypeVariable t, P p) {
             scan(t.getLowerBound(), p);
             scan(t.getUpperBound(), p);
@@ -254,36 +376,194 @@
     }
 
     /** Annotation to identify test cases. */
+    @Repeatable(Tests.class)
     @interface Test {
         /** Where to look for the annotation, expressed as a scan index. */
-        int posn();
+        int[] posn();
         /** The annotation to look for. */
         Class<? extends Annotation> annoType();
         /** The string representation of the annotation's value. */
         String expect();
     }
 
+    @interface Tests {
+        Test[] value();
+    }
+
     /** Type annotation to use in test cases. */
     @Target(ElementType.TYPE_USE)
     public @interface TA {
         int value();
     }
+    @Target(ElementType.TYPE_USE)
+    public @interface TB {
+        int value();
+    }
+
+    // Test cases
+
+    // TODO: add more cases for arrays
+    //       all annotated
+    //       all but one annotated
+    //             vary position of one not annotated
+    //       only one annotated
+    //             vary position of one annotated
+    //       the three above with the corner case of the ambiguos decl + type anno added
 
     @Test(posn=0, annoType=TA.class, expect="1")
     public @TA(1) int f1;
 
+    @Test(posn=0, annoType=TA.class, expect="11")
+    @TA(11) public int f11;
+
+    @Test(posn=1, annoType=TA.class, expect="111")
+    @TA(111) public int [] f111;
+
+    @Test(posn=1, annoType=TA.class, expect="1120")
+    @Test(posn=0, annoType=TB.class, expect="1121")
+    @TA(1120) public int @TB(1121) [] f112;
+
+    @Test(posn=0, annoType=TB.class, expect="11211")
+    @Test(posn=1, annoType=TA.class, expect="11200")
+    public @TA(11200) int @TB(11211) [] f112b;
+
+    @Test(posn=1, annoType=TB.class, expect="1131")
+    @Test(posn=2, annoType=TA.class, expect="1130")
+    @TA(1130) public int [] @TB(1131) [] f113;
+
+    @Test(posn=5, annoType=TA.class, expect="12")
+    public @TA(12) int [] [] [] [] [] f12;
+
+    @Test(posn=6, annoType=TA.class, expect="13")
+    public @TA(13) int [] [] [] [] [] [] f13;
+
+    @Test(posn=7, annoType=TA.class, expect="14")
+    @TA(14) public int [] [] [] [] [] [] [] f14;
+
+    @Test(posn=6, annoType=TA.class, expect="150")
+    @Test(posn=7, annoType=TB.class, expect="151")
+    @TB(151) public int [] [] [] [] [] [] @TA(150) [] f15;
+
+    @Test(posn=0, annoType=TB.class, expect="1511")
+    @Test(posn=3, annoType=TA.class, expect="1512")
+    @Test(posn=6, annoType=TA.class, expect="150")
+    @Test(posn=7, annoType=TB.class, expect="151")
+    @TB(151) public int @TB(1511) [] [] [] @TA(1512) [] [] [] @TA(150) [] f15b;
+
+    @Test(posn=0, annoType=TB.class, expect="1521")
+    @Test(posn=3, annoType=TA.class, expect="1522")
+    @Test(posn=6, annoType=TA.class, expect="152")
+    public int @TB(1521) [] [] [] @TA(1522) [] [] [] @TA(152) [] f15c;
+
+    @Test(posn=5, annoType=TA.class, expect="160")
+    @Test(posn=6, annoType=TB.class, expect="161")
+    public int [] [] [] [] [] @TA(160) [] @TB(161) [] f16;
+
     @Test(posn=0, annoType=TA.class, expect="2")
     public int @TA(2) [] f2;
 
+    @Test(posn=0, annoType=TB.class, expect="33")
     @Test(posn=1, annoType=TA.class, expect="3")
-    public @TA(3) int [] f3;
+    public @TA(3) int @TB(33) [] f3;
 
-    @Test(posn=1, annoType=TA.class, expect="4")
+    @Test(posn=2, annoType=TA.class, expect="4")
     public int m1(@TA(4) float a) throws Exception { return 0; }
 
-    @Test(posn=2, annoType=TA.class, expect="5")
+    @Test(posn=1, annoType=TA.class, expect="5")
     public @TA(5) int m2(float a) throws Exception { return 0; }
 
     @Test(posn=3, annoType=TA.class, expect="6")
     public int m3(float a) throws @TA(6) Exception { return 0; }
+
+    // Also tests that a decl anno on a typevar doesn't show up on the Type
+    @Test(posn=7, annoType=TA.class, expect="8")
+    public <@TA(7) M> M m4(@TA(8) float a) throws Exception { return null; }
+
+    // Also tests that a decl anno on a typevar doesn't show up on the Type
+    @Test(posn=4, annoType=TA.class, expect="10")
+    public class Inner1<@TA(9) S> extends @TA(10) Object implements Cloneable {}
+
+    // Also tests that a decl anno on a typevar doesn't show up on the Type
+    @Test(posn=5, annoType=TA.class, expect="12")
+    public class Inner2<@TA(11) S> extends Object implements @TA(12) Cloneable {}
+
+    @Test(posn={3,6}, annoType=TA.class, expect="13")
+    public <M extends @TA(13) Object> M m5(float a) { return null; }
+
+    @Test(posn=3, annoType=TA.class, expect="14")
+    public class Inner3<QQQ extends @TA(14) Map> {}
+
+    @Test(posn=4, annoType=TA.class, expect="15")
+    public class Inner4<T extends @TA(15) Object & Cloneable & Serializable> {}
+
+    @Test(posn=5, annoType=TA.class, expect="16")
+    public class Inner5<T extends Object & @TA(16) Cloneable & Serializable> {}
+
+    @Test(posn=7, annoType=TA.class, expect="17")
+    public class Inner6<T extends Object & Cloneable & @TA(17) Serializable> {}
+
+    // Test annotated bounds
+
+    @Test(posn=1, annoType=TA.class, expect="18")
+    public Set<@TA(18) ? extends Object> f4;
+
+    @Test(posn=2, annoType=TA.class, expect="19")
+    public Set<? extends @TA(19) Object> f5;
+
+    @Test(posn=3, annoType=TA.class, expect="20")
+    public Set<? extends Set<@TA(20) ? extends Object>> f6;
+
+    @Test(posn=4, annoType=TA.class, expect="21")
+    public Set<? extends Set<? extends @TA(21) Object>> f7;
+
+    @Test(posn=1, annoType=TA.class, expect="22")
+    public Set<@TA(22) ?> f8;
+
+    @Test(posn=1, annoType=TA.class, expect="23")
+    public Set<@TA(23) ? super Object> f9;
+
+    // Test type use annotations on uses of type variables
+    @Test(posn=5, annoType = TA.class, expect = "25")
+    @Test(posn=5, annoType = TB.class, expect = "26")
+    <T> void m6(@TA(25) @TB(26) T t) { }
+
+    class Inner7<T> {
+        @Test(posn=0, annoType = TA.class, expect = "30")
+        @Test(posn=0, annoType = TB.class, expect = "31")
+        @TA(30) @TB(31) T f;
+    }
+
+    // Test type use annotations on uses of type variables
+    @Test(posn=5, annoType = TB.class, expect = "41")
+    <@TA(40) T> void m7(@TB(41) T t) { }
+
+    class Inner8<@TA(50) T> {
+        @Test(posn=0, annoType = TB.class, expect = "51")
+        @TB(51) T f;
+    }
+
+    // Test type use annotations on uses of Class types
+    @Test(posn=5, annoType = TA.class, expect = "60")
+    @Test(posn=5, annoType = TB.class, expect = "61")
+    <T> void m60(@TA(60) @TB(61) String t) { }
+
+    class Inner70<T> {
+        @Test(posn=0, annoType = TA.class, expect = "70")
+        @Test(posn=0, annoType = TB.class, expect = "71")
+        @TA(70) @TB(71) String f;
+    }
+
+    // Test type use annotations on uses of type variables
+    @Test(posn=5, annoType = TB.class, expect = "81")
+    <@TA(80) T> void m80(@TB(81) String t) { }
+
+    class Inner90<@TA(90) T> {
+        @Test(posn=0, annoType = TB.class, expect = "91")
+        @TB(91) String f;
+    }
+
+    // Recursive bound
+    @Test(posn=4, annoType = TB.class, expect = "100")
+    class Inner100<T extends Inner100<@TB(100) T>> {
+    }
 }