| /* |
| * Copyright (c) 2014, 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 8042239 |
| * @summary Verify that TreeMaker.Type(Type) can handle all reasonable 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 MakeTypeTest |
| * @compile/process/ref=MakeTypeTest.out -XDaccessInternalAPI -processor MakeTypeTest MakeTypeTest.java |
| */ |
| |
| import java.lang.annotation.*; |
| import java.util.*; |
| |
| import javax.annotation.processing.RoundEnvironment; |
| import javax.lang.model.element.*; |
| import javax.lang.model.type.*; |
| |
| import com.sun.source.tree.*; |
| import com.sun.source.util.*; |
| import com.sun.tools.javac.api.JavacTrees; |
| import com.sun.tools.javac.code.Type; |
| import com.sun.tools.javac.code.Type.ClassType; |
| import com.sun.tools.javac.processing.JavacProcessingEnvironment; |
| import com.sun.tools.javac.tree.JCTree.JCExpression; |
| import com.sun.tools.javac.tree.TreeMaker; |
| import com.sun.tools.javac.util.*; |
| import com.sun.tools.javac.util.List; |
| |
| public class MakeTypeTest extends JavacTestingAbstractProcessor { |
| @Override |
| public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { |
| if (!roundEnv.processingOver()) |
| return false; |
| |
| JavacTask.instance(processingEnv).addTaskListener(new TaskListener() { |
| @Override |
| public void started(TaskEvent e) { |
| } |
| @Override |
| public void finished(TaskEvent e) { |
| if (e.getKind() == TaskEvent.Kind.ANALYZE && |
| e.getTypeElement().getQualifiedName().contentEquals("MakeTypeTest")) { |
| doTest(); |
| } |
| } |
| }); |
| |
| return false; |
| } |
| |
| void doTest() { |
| //go through this file, look for @TestType and verify TreeMaker.Type behavior: |
| Context ctx = ((JavacProcessingEnvironment) processingEnv).getContext(); |
| JavacTrees trees = JavacTrees.instance(ctx); |
| TypeElement testType = processingEnv.getElementUtils().getTypeElement("MakeTypeTest"); |
| TreePath path = trees.getPath(testType); |
| |
| Set<TypeKind> unseenTypeKinds = EnumSet.allOf(TypeKind.class); |
| |
| new TreePathScanner<Void, Void>() { |
| @Override |
| public Void visitVariable(VariableTree node, Void p) { |
| handleDecl(new TreePath(getCurrentPath(), node.getType())); |
| return super.visitVariable(node, p); |
| } |
| |
| @Override |
| public Void visitMethod(MethodTree node, Void p) { |
| if (node.getReturnType() != null) |
| handleDecl(new TreePath(getCurrentPath(), node.getReturnType())); |
| return super.visitMethod(node, p); |
| } |
| |
| @Override |
| public Void visitTypeParameter(TypeParameterTree node, Void p) { |
| TypeVariable type = (TypeVariable) trees.getTypeMirror(getCurrentPath()); |
| TreePath aBoundPath = new TreePath(getCurrentPath(), node.getBounds().get(0)); |
| handleDecl(aBoundPath, (Type) type.getUpperBound()); |
| return super.visitTypeParameter(node, p); |
| } |
| |
| void handleDecl(TreePath typePath) { |
| handleDecl(typePath, (Type) trees.getTypeMirror(typePath)); |
| } |
| |
| void handleDecl(TreePath typePath, Type type) { |
| Element decl = trees.getElement(typePath.getParentPath()); |
| TestType testType = decl.getAnnotation(TestType.class); |
| |
| if (testType == null) return ; |
| |
| if (testType.nested() >= 0) { |
| ClassType ct = (ClassType) type; |
| type = ct.getTypeArguments().get(testType.nested()); |
| } |
| |
| JCExpression typeExpr = TreeMaker.instance(ctx).Type(type); |
| |
| if (!typeExpr.getKind().equals(testType.expectedKind())) { |
| throw new IllegalStateException("was=" + typeExpr + ", kind=" + |
| typeExpr.getKind() + "; expected kind: " + |
| testType.expectedKind() + "; type=" + type); |
| } |
| unseenTypeKinds.remove(type.getKind()); |
| } |
| |
| }.scan(path, null); |
| |
| unseenTypeKinds.removeAll(Arrays.asList(TypeKind.NONE, TypeKind.NULL, TypeKind.ERROR, |
| TypeKind.PACKAGE, TypeKind.EXECUTABLE, TypeKind.OTHER, TypeKind.MODULE)); |
| |
| if (!unseenTypeKinds.isEmpty()) |
| throw new IllegalStateException("Unhandled types=" + unseenTypeKinds); |
| |
| System.err.println("done."); |
| } |
| |
| //the following defines the Types that should be passed into TreeMaker.Type and |
| //the expected resulting Tree kind: |
| |
| @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) |
| boolean f1; |
| @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) |
| byte f2; |
| @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) |
| char f3; |
| @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) |
| double f4; |
| @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) |
| float f5; |
| @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) |
| int f6; |
| @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) |
| long f7; |
| @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) |
| short f8; |
| @TestType(expectedKind=Tree.Kind.PARAMETERIZED_TYPE) |
| List<? extends String> f9; |
| @TestType(expectedKind=Tree.Kind.ARRAY_TYPE) |
| int[] fa; |
| @TestType(expectedKind=Tree.Kind.EXTENDS_WILDCARD, nested = 0) |
| List<? extends String> fb; |
| @TestType(expectedKind=Tree.Kind.SUPER_WILDCARD, nested = 0) |
| List<? super String> fc; |
| @TestType(expectedKind=Tree.Kind.UNBOUNDED_WILDCARD, nested = 0) |
| List<?> fd; |
| |
| @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE) |
| void voidMethod() { |
| try { |
| voidMethod(); |
| } catch (@TestType(expectedKind=Tree.Kind.UNION_TYPE) NullPointerException | |
| IllegalStateException ex) { |
| } |
| } |
| |
| class WithTypeParam<@TestType(expectedKind=Tree.Kind.INTERSECTION_TYPE) |
| T extends CharSequence & Runnable> { |
| @TestType(expectedKind=Tree.Kind.IDENTIFIER) |
| T voidMethod() { |
| return null; |
| } |
| } |
| |
| } |
| |
| //TreeMaker.Type will be tested for the type the element annotated by this annotation |
| @Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, |
| ElementType.PARAMETER, ElementType.TYPE_PARAMETER}) |
| @interface TestType { |
| //the expected Tree kind of the Tree that will be returned from TreeMaker.Type for the type |
| public Tree.Kind expectedKind(); |
| //if >=0, the current type will be interpreted as a ClassType and the type to test will be |
| //the given type argument: |
| public int nested() default -1; |
| } |