/* * 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 * 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 8013852 8031744 * @summary Annotations on types * @library /tools/javac/lib * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.code * jdk.compiler/com.sun.tools.javac.processing * jdk.compiler/com.sun.tools.javac.tree * jdk.compiler/com.sun.tools.javac.util * @build JavacTestingAbstractProcessor DPrinter BasicAnnoTests * @compile/process -XDaccessInternalAPI -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; import javax.lang.model.AnnotatedConstruct; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; 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.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. */ public class BasicAnnoTests extends JavacTestingAbstractProcessor { DPrinter dprinter; PrintWriter out; boolean verbose = true; @Override public void init(ProcessingEnvironment pEnv) { super.init(pEnv); dprinter = new DPrinter(((JavacProcessingEnvironment) pEnv).getContext()); out = dprinter.out; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { TestElementScanner s = new TestElementScanner(); for (Element e: roundEnv.getRootElements()) { s.scan(e, null); } return true; } void error(Element e, String msg) { messager.printMessage(Kind.ERROR, msg, e); errors++; } int errors; /** * Scan an element looking for declarations annotated with @Test. * Run a TestTypeScanner on the annotations that are found. */ 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, tests, types); s.test(elem.asType()); out.println(); } return super.scan(elem, ignore); } } /** * Scan the type of an element, looking for an annotation * to match the expected annotation specified in the @Test annotation. */ class TestTypeScanner extends TypeScanner<Void, Void> { Element elem; NavigableMap<Integer, AnnotationMirror> toBeFound; int count = 0; Set<TypeMirror> seen = new HashSet<>(); TestTypeScanner(Element elem, List<AnnotationMirror> tests, Types types) { super(types); this.elem = elem; 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 (!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 { 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); } } return DEFAULT_VALUE; } private void rescue() { while (toBeFound.size() > 0 && toBeFound.firstKey() >= count) toBeFound.pollFirstEntry(); } } /** 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"); 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. */ static String getExpect(AnnotationMirror test) { AnnotationValue v = getValue(test, "expect"); return (String) v.getValue(); } /** Get the annoType value from an @Test annotation mirror. */ static String getAnnoType(AnnotationMirror test) { AnnotationValue v = getValue(test, "annoType"); TypeMirror m = (TypeMirror) v.getValue(); return m.toString(); } /** * Get a specific annotation mirror from an annotated construct. */ static AnnotationMirror getAnnotation(AnnotatedConstruct e, String name) { for (AnnotationMirror m: e.getAnnotationMirrors()) { TypeElement te = (TypeElement) m.getAnnotationType().asElement(); if (te.getQualifiedName().contentEquals(name)) { return m; } } 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. */ static AnnotationValue getValue(AnnotationMirror anno, String name) { Map<? extends ExecutableElement, ? extends AnnotationValue> map = anno.getElementValues(); for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e: map.entrySet()) { if (e.getKey().getSimpleName().contentEquals(name)) { return e.getValue(); } } return null; } /** * The Language Model API does not provide a type scanner, so provide * 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); return super.visitArray(t, p); } @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(" 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); return super.visitTypeVariable(t, p); } @Override public R visitWildcard(WildcardType t, P p) { scan(t.getExtendsBound(), p); scan(t.getSuperBound(), p); return super.visitWildcard(t, p); } R scan(TypeMirror t) { return scan(t, null); } R scan(TypeMirror t, P p) { return (t == null) ? DEFAULT_VALUE : t.accept(this, p); } R scan(Iterable<? extends TypeMirror> iter, P p) { if (iter == null) return DEFAULT_VALUE; R result = DEFAULT_VALUE; for (TypeMirror t: iter) result = scan(t, p); return result; } } /** Annotation to identify test cases. */ @Repeatable(Tests.class) @interface Test { /** Where to look for the annotation, expressed as a scan index. */ 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 @TB(33) [] f3; @Test(posn=2, annoType=TA.class, expect="4") public int m1(@TA(4) float a) throws Exception { return 0; } @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>> { } }