1 /* 2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8042239 27 * @summary Verify that TreeMaker.Type(Type) can handle all reasonable types 28 * @enablePreview 29 * @library /tools/javac/lib 30 * @modules jdk.compiler/com.sun.tools.javac.api 31 * jdk.compiler/com.sun.tools.javac.code 32 * jdk.compiler/com.sun.tools.javac.processing 33 * jdk.compiler/com.sun.tools.javac.tree 34 * jdk.compiler/com.sun.tools.javac.util 35 * @build JavacTestingAbstractProcessor MakeTypeTest 36 * @compile/process/ref=MakeTypeTest.out -XDaccessInternalAPI -processor MakeTypeTest MakeTypeTest.java 37 */ 38 39 import java.lang.annotation.*; 40 import java.util.*; 41 42 import javax.annotation.processing.RoundEnvironment; 43 import javax.lang.model.element.*; 44 import javax.lang.model.type.*; 45 46 import com.sun.source.tree.*; 47 import com.sun.source.util.*; 48 import com.sun.tools.javac.api.JavacTrees; 49 import com.sun.tools.javac.code.Type; 50 import com.sun.tools.javac.code.Type.ClassType; 51 import com.sun.tools.javac.processing.JavacProcessingEnvironment; 52 import com.sun.tools.javac.tree.JCTree.JCExpression; 53 import com.sun.tools.javac.tree.TreeMaker; 54 import com.sun.tools.javac.util.*; 55 import com.sun.tools.javac.util.List; 56 57 public class MakeTypeTest extends JavacTestingAbstractProcessor { 58 @Override 59 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 60 if (!roundEnv.processingOver()) 61 return false; 62 63 JavacTask.instance(processingEnv).addTaskListener(new TaskListener() { 64 @Override 65 public void started(TaskEvent e) { 66 } 67 @Override 68 public void finished(TaskEvent e) { 69 if (e.getKind() == TaskEvent.Kind.ANALYZE && 70 e.getTypeElement().getQualifiedName().contentEquals("MakeTypeTest")) { 71 doTest(); 72 } 73 } 74 }); 75 76 return false; 77 } 78 79 void doTest() { 80 //go through this file, look for @TestType and verify TreeMaker.Type behavior: 81 Context ctx = ((JavacProcessingEnvironment) processingEnv).getContext(); 82 JavacTrees trees = JavacTrees.instance(ctx); 83 TypeElement testType = processingEnv.getElementUtils().getTypeElement("MakeTypeTest"); 84 TreePath path = trees.getPath(testType); 85 86 Set<TypeKind> unseenTypeKinds = EnumSet.allOf(TypeKind.class); 87 88 new TreePathScanner<Void, Void>() { 89 @Override 90 public Void visitVariable(VariableTree node, Void p) { 91 handleDecl(new TreePath(getCurrentPath(), node.getType())); 92 return super.visitVariable(node, p); 93 } 94 95 @Override 96 public Void visitMethod(MethodTree node, Void p) { 97 if (node.getReturnType() != null) 98 handleDecl(new TreePath(getCurrentPath(), node.getReturnType())); 99 return super.visitMethod(node, p); 100 } 101 102 @Override 103 public Void visitTypeParameter(TypeParameterTree node, Void p) { 104 TypeVariable type = (TypeVariable) trees.getTypeMirror(getCurrentPath()); 105 TreePath aBoundPath = new TreePath(getCurrentPath(), node.getBounds().get(0)); 106 handleDecl(aBoundPath, (Type) type.getUpperBound()); 107 return super.visitTypeParameter(node, p); 108 } 109 110 void handleDecl(TreePath typePath) { 111 handleDecl(typePath, (Type) trees.getTypeMirror(typePath)); 112 } 113 114 void handleDecl(TreePath typePath, Type type) { 115 Element decl = trees.getElement(typePath.getParentPath()); 116 TestType testType = decl.getAnnotation(TestType.class); 117 118 if (testType == null) return ; 119 120 if (testType.nested() >= 0) { 121 ClassType ct = (ClassType) type; 122 type = ct.getTypeArguments().get(testType.nested()); 123 } 124 125 JCExpression typeExpr = TreeMaker.instance(ctx).Type(type); 126 127 if (!typeExpr.getKind().equals(testType.expectedKind())) { 128 throw new IllegalStateException("was=" + typeExpr + ", kind=" + 129 typeExpr.getKind() + "; expected kind: " + 130 testType.expectedKind() + "; type=" + type); 131 } 132 unseenTypeKinds.remove(type.getKind()); 133 } 134 135 }.scan(path, null); 136 137 unseenTypeKinds.removeAll(Arrays.asList(TypeKind.NONE, TypeKind.NULL, TypeKind.ERROR, 138 TypeKind.PACKAGE, TypeKind.EXECUTABLE, TypeKind.OTHER, TypeKind.MODULE)); 139 140 if (!unseenTypeKinds.isEmpty()) 141 throw new IllegalStateException("Unhandled types=" + unseenTypeKinds); 142 143 System.err.println("done."); 144 } 145 146 //the following defines the Types that should be passed into TreeMaker.Type and 147 //the expected resulting Tree kind: 148 149 @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) 150 boolean f1; 151 @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) 152 byte f2; 153 @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) 154 char f3; 155 @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) 156 double f4; 157 @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) 158 float f5; 159 @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) 160 int f6; 161 @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) 162 long f7; 163 @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) 164 short f8; 165 @TestType(expectedKind=Tree.Kind.PARAMETERIZED_TYPE) 166 List<? extends String> f9; 167 @TestType(expectedKind=Tree.Kind.ARRAY_TYPE) 168 int[] fa; 169 @TestType(expectedKind=Tree.Kind.EXTENDS_WILDCARD, nested = 0) 170 List<? extends String> fb; 171 @TestType(expectedKind=Tree.Kind.SUPER_WILDCARD, nested = 0) 172 List<? super String> fc; 173 @TestType(expectedKind=Tree.Kind.UNBOUNDED_WILDCARD, nested = 0) 174 List<?> fd; 175 176 @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) 177 void voidMethod() { 178 try { 179 voidMethod(); 180 } catch (@TestType(expectedKind=Tree.Kind.UNION_TYPE) NullPointerException | 181 IllegalStateException ex) { 182 } 183 } 184 185 class WithTypeParam<@TestType(expectedKind=Tree.Kind.INTERSECTION_TYPE) 186 T extends CharSequence & Runnable> { 187 @TestType(expectedKind=Tree.Kind.IDENTIFIER) 188 T voidMethod() { 189 return null; 190 } 191 } 192 193 } 194 195 //TreeMaker.Type will be tested for the type the element annotated by this annotation 196 @Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, 197 ElementType.PARAMETER, ElementType.TYPE_PARAMETER}) 198 @interface TestType { 199 //the expected Tree kind of the Tree that will be returned from TreeMaker.Type for the type 200 public Tree.Kind expectedKind(); 201 //if >=0, the current type will be interpreted as a ClassType and the type to test will be 202 //the given type argument: 203 public int nested() default -1; 204 }