BasicAnnoTests.java revision 2628:8df25ec8c930
143561Skato/*
243561Skato * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
343561Skato * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
443561Skato *
543561Skato * This code is free software; you can redistribute it and/or modify it
643561Skato * under the terms of the GNU General Public License version 2 only, as
743561Skato * published by the Free Software Foundation.
843561Skato *
943561Skato * This code is distributed in the hope that it will be useful, but WITHOUT
1043561Skato * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1143561Skato * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1243561Skato * version 2 for more details (a copy is included in the LICENSE file that
1343561Skato * accompanied this code).
1443561Skato *
1543561Skato * You should have received a copy of the GNU General Public License version
1643561Skato * 2 along with this work; if not, write to the Free Software Foundation,
1750477Speter * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
1843561Skato *
1943561Skato * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2043561Skato * or visit www.oracle.com if you need additional information or have any
2143561Skato * questions.
2243561Skato */
2343561Skato
24229501Sjhb/*
2543561Skato * @test
2643561Skato * @bug     8013852
2743561Skato * @summary Annotations on types
2843561Skato * @library /tools/javac/lib
2943561Skato * @ignore 8057688 type annotations in type argument position are lost
3043561Skato * @ignore 8031744 Annotations on many Language Model elements are not returned
3143561Skato * @build JavacTestingAbstractProcessor DPrinter BasicAnnoTests
3243561Skato * @compile/process -processor BasicAnnoTests -proc:only BasicAnnoTests.java
3343561Skato */
3443561Skato
3543561Skatoimport java.io.PrintWriter;
3643561Skatoimport java.lang.annotation.Annotation;
3743561Skatoimport java.lang.annotation.ElementType;
3843561Skatoimport java.lang.annotation.Target;
3943561Skatoimport java.util.Map;
4043561Skatoimport java.util.Set;
4143561Skato
4243561Skatoimport javax.annotation.processing.ProcessingEnvironment;
4343561Skatoimport javax.annotation.processing.RoundEnvironment;
4443561Skatoimport javax.lang.model.AnnotatedConstruct;
4543561Skatoimport javax.lang.model.element.AnnotationMirror;
4643561Skatoimport javax.lang.model.element.AnnotationValue;
4743561Skatoimport javax.lang.model.element.Element;
4843561Skatoimport javax.lang.model.element.ExecutableElement;
4943561Skatoimport javax.lang.model.element.TypeElement;
5043561Skatoimport javax.lang.model.type.ArrayType;
5143561Skatoimport javax.lang.model.type.ExecutableType;
5243561Skatoimport javax.lang.model.type.TypeMirror;
5343561Skatoimport javax.lang.model.type.TypeVariable;
5443561Skatoimport javax.lang.model.type.WildcardType;
5543561Skatoimport javax.tools.Diagnostic.Kind;
5643561Skato
5743561Skatoimport com.sun.tools.javac.code.Symbol;
5843561Skatoimport com.sun.tools.javac.code.Type;
5943561Skatoimport com.sun.tools.javac.processing.JavacProcessingEnvironment;
6043561Skato
61229501Sjhb/**
62229501Sjhb * The test scans this file looking for test cases annotated with @Test.
63229501Sjhb */
6443561Skatopublic class BasicAnnoTests extends JavacTestingAbstractProcessor {
6543561Skato    DPrinter dprinter;
6643561Skato    PrintWriter out;
6743561Skato    boolean verbose = true;
68
69    @Override
70    public void init(ProcessingEnvironment pEnv) {
71        super.init(pEnv);
72        dprinter = new DPrinter(((JavacProcessingEnvironment) pEnv).getContext());
73        out = dprinter.out;
74    }
75
76    @Override
77    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
78        TestElementScanner s = new TestElementScanner();
79        for (Element e: roundEnv.getRootElements()) {
80            s.scan(e);
81        }
82        return true;
83    }
84
85    void error(Element e, String msg) {
86        messager.printMessage(Kind.ERROR, msg, e);
87        errors++;
88    }
89
90    int errors;
91
92    /**
93     * Scan an element looking for declarations annotated with @Test.
94     * Run a TestTypeScanner on the annotations that are found.
95     */
96    class TestElementScanner extends ElementScanner<Void,Void> {
97        public Void scan(Element elem, Void ignore) {
98            AnnotationMirror test = getAnnotation(elem, Test.class.getName().replace('$', '.'));
99            if (test != null) {
100                out.println("Test: " + elem + " " + test);
101                TestTypeScanner s = new TestTypeScanner(elem, test);
102                s.scan(elem.asType(), null);
103                if (getPosn(test) >= s.count)
104                    error(elem, "position " + getPosn(test) + " not found");
105                if (!s.found) {
106                    dprinter.printSymbol("element", (Symbol) elem);
107                    dprinter.printType("type", (Type) elem.asType());
108                }
109                out.println();
110            }
111            return super.scan(elem, ignore);
112        }
113    }
114
115    /**
116     * Scan the type of an element, looking for an annotation
117     * to match the expected annotation specified in the @Test annotation.
118     */
119    class TestTypeScanner extends TypeScanner<Void, Void> {
120        Element elem;
121        AnnotationMirror test;
122        int count = 0;
123        boolean found = false;
124
125        TestTypeScanner(Element elem, AnnotationMirror test) {
126            this.elem = elem;
127            this.test = test;
128        }
129
130        @Override
131        Void scan(TypeMirror t, Void ignore) {
132            if (t == null)
133                return DEFAULT_VALUE;
134            if (verbose)
135                out.println("scan " + count + ": " + t);
136            if (count == getPosn(test)) {
137                String annoType = getAnnoType(test);
138                AnnotationMirror anno = getAnnotation(t, annoType);
139                if (anno == null) {
140                    error(elem, "annotation not found on " + count + ": " + t);
141                } else {
142                    String v = getValue(anno, "value").toString();
143                    if (v.equals(getExpect(test))) {
144                        out.println("found " + anno + " as expected");
145                        found = true;
146                    } else {
147                        error(elem, "Unexpected value: " + v + ", expected: " + getExpect(test));
148                    }
149                }
150            }
151            count++;
152            return super.scan(t, ignore);
153        }
154    }
155
156    /** Get the position value from an @Test annotation mirror. */
157    static int getPosn(AnnotationMirror test) {
158        AnnotationValue v = getValue(test, "posn");
159        return (Integer) v.getValue();
160    }
161
162    /** Get the expect value from an @Test annotation mirror. */
163    static String getExpect(AnnotationMirror test) {
164        AnnotationValue v = getValue(test, "expect");
165        return (String) v.getValue();
166    }
167
168    /** Get the annoType value from an @Test annotation mirror. */
169    static String getAnnoType(AnnotationMirror test) {
170        AnnotationValue v = getValue(test, "annoType");
171        TypeMirror m = (TypeMirror) v.getValue();
172        return m.toString();
173    }
174
175    /**
176     * Get a specific annotation mirror from an annotated construct.
177     */
178    static AnnotationMirror getAnnotation(AnnotatedConstruct e, String name) {
179        for (AnnotationMirror m: e.getAnnotationMirrors()) {
180            TypeElement te = (TypeElement) m.getAnnotationType().asElement();
181            if (te.getQualifiedName().contentEquals(name)) {
182                return m;
183            }
184        }
185        return null;
186    }
187
188    /**
189     * Get a specific value from an annotation mirror.
190     */
191    static AnnotationValue getValue(AnnotationMirror anno, String name) {
192        Map<? extends ExecutableElement, ? extends AnnotationValue> map = anno.getElementValues();
193        for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e: map.entrySet()) {
194            if (e.getKey().getSimpleName().contentEquals(name)) {
195                return e.getValue();
196            }
197        }
198        return null;
199    }
200
201    /**
202     * The Language Model API does not provide a type scanner, so provide
203     * one sufficient for our needs.
204     */
205    static class TypeScanner<R, P> extends SimpleTypeVisitor<R, P> {
206        @Override
207        public R visitArray(ArrayType t, P p) {
208            scan(t.getComponentType(), p);
209            return super.visitArray(t, p);
210        }
211
212        @Override
213        public R visitExecutable(ExecutableType t, P p) {
214            scan(t.getReceiverType());
215            //out.println("  params: " + t.getParameterTypes());
216            scan(t.getParameterTypes(), p);
217            //out.println("  return: " + t.getReturnType());
218            scan(t.getReturnType(), p);
219            //out.println("  throws: " + t.getThrownTypes());
220            scan(t.getThrownTypes(), p);
221            return super.visitExecutable(t, p);
222        }
223
224        @Override
225        public R visitTypeVariable(TypeVariable t, P p) {
226            scan(t.getLowerBound(), p);
227            scan(t.getUpperBound(), p);
228            return super.visitTypeVariable(t, p);
229        }
230
231        @Override
232        public R visitWildcard(WildcardType t, P p) {
233            scan(t.getExtendsBound(), p);
234            scan(t.getSuperBound(), p);
235            return super.visitWildcard(t, p);
236        }
237
238        R scan(TypeMirror t) {
239            return scan(t, null);
240        }
241
242        R scan(TypeMirror t, P p) {
243            return (t == null) ? DEFAULT_VALUE : t.accept(this, p);
244        }
245
246        R scan(Iterable<? extends TypeMirror> iter, P p) {
247            if (iter == null)
248                return DEFAULT_VALUE;
249            R result = DEFAULT_VALUE;
250            for (TypeMirror t: iter)
251                result = scan(t, p);
252            return result;
253        }
254    }
255
256    /** Annotation to identify test cases. */
257    @interface Test {
258        /** Where to look for the annotation, expressed as a scan index. */
259        int posn();
260        /** The annotation to look for. */
261        Class<? extends Annotation> annoType();
262        /** The string representation of the annotation's value. */
263        String expect();
264    }
265
266    /** Type annotation to use in test cases. */
267    @Target(ElementType.TYPE_USE)
268    public @interface TA {
269        int value();
270    }
271
272    @Test(posn=0, annoType=TA.class, expect="1")
273    public @TA(1) int f1;
274
275    @Test(posn=0, annoType=TA.class, expect="2")
276    public int @TA(2) [] f2;
277
278    @Test(posn=1, annoType=TA.class, expect="3")
279    public @TA(3) int [] f3;
280
281    @Test(posn=1, annoType=TA.class, expect="4")
282    public int m1(@TA(4) float a) throws Exception { return 0; }
283
284    @Test(posn=2, annoType=TA.class, expect="5")
285    public @TA(5) int m2(float a) throws Exception { return 0; }
286
287    @Test(posn=3, annoType=TA.class, expect="6")
288    public int m3(float a) throws @TA(6) Exception { return 0; }
289}
290