blob: 85a6f6af9d89684c2c3cb46b112de189dec25acf [file] [log] [blame]
/*
* 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>> {
}
}