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  * @library /tools/javac/lib
 29  * @modules jdk.compiler/com.sun.tools.javac.api
 30  *          jdk.compiler/com.sun.tools.javac.code
 31  *          jdk.compiler/com.sun.tools.javac.processing
 32  *          jdk.compiler/com.sun.tools.javac.tree
 33  *          jdk.compiler/com.sun.tools.javac.util
 34  * @build JavacTestingAbstractProcessor MakeTypeTest
 35  * @compile/process/ref=MakeTypeTest.out -XDaccessInternalAPI -processor MakeTypeTest MakeTypeTest.java
 36  */
 37 
 38 import java.lang.annotation.*;
 39 import java.util.*;
 40 
 41 import javax.annotation.processing.RoundEnvironment;
 42 import javax.lang.model.element.*;
 43 import javax.lang.model.type.*;
 44 
 45 import com.sun.source.tree.*;
 46 import com.sun.source.util.*;
 47 import com.sun.tools.javac.api.JavacTrees;
 48 import com.sun.tools.javac.code.Type;
 49 import com.sun.tools.javac.code.Type.ClassType;
 50 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
 51 import com.sun.tools.javac.tree.JCTree.JCExpression;
 52 import com.sun.tools.javac.tree.TreeMaker;
 53 import com.sun.tools.javac.util.*;
 54 import com.sun.tools.javac.util.List;
 55 
 56 public class MakeTypeTest extends JavacTestingAbstractProcessor {
 57     @Override
 58     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 59         if (!roundEnv.processingOver())
 60             return false;
 61 
 62         JavacTask.instance(processingEnv).addTaskListener(new TaskListener() {
 63             @Override
 64             public void started(TaskEvent e) {
 65             }
 66             @Override
 67             public void finished(TaskEvent e) {
 68                 if (e.getKind() == TaskEvent.Kind.ANALYZE &&
 69                     e.getTypeElement().getQualifiedName().contentEquals("MakeTypeTest")) {
 70                     doTest();
 71                 }
 72             }
 73         });
 74 
 75         return false;
 76     }
 77 
 78     void doTest() {
 79         //go through this file, look for @TestType and verify TreeMaker.Type behavior:
 80         Context ctx = ((JavacProcessingEnvironment) processingEnv).getContext();
 81         JavacTrees trees = JavacTrees.instance(ctx);
 82         TypeElement testType = processingEnv.getElementUtils().getTypeElement("MakeTypeTest");
 83         TreePath path = trees.getPath(testType);
 84 
 85         Set<TypeKind> unseenTypeKinds = EnumSet.allOf(TypeKind.class);
 86 
 87         new TreePathScanner<Void, Void>() {
 88             @Override
 89             public Void visitVariable(VariableTree node, Void p) {
 90                 handleDecl(new TreePath(getCurrentPath(), node.getType()));
 91                 return super.visitVariable(node, p);
 92             }
 93 
 94             @Override
 95             public Void visitMethod(MethodTree node, Void p) {
 96                 if (node.getReturnType() != null)
 97                     handleDecl(new TreePath(getCurrentPath(), node.getReturnType()));
 98                 return super.visitMethod(node, p);
 99             }
100 
101             @Override
102             public Void visitTypeParameter(TypeParameterTree node, Void p) {
103                 TypeVariable type = (TypeVariable) trees.getTypeMirror(getCurrentPath());
104                 TreePath aBoundPath = new TreePath(getCurrentPath(), node.getBounds().get(0));
105                 handleDecl(aBoundPath, (Type) type.getUpperBound());
106                 return super.visitTypeParameter(node, p);
107             }
108 
109             void handleDecl(TreePath typePath) {
110                 handleDecl(typePath, (Type) trees.getTypeMirror(typePath));
111             }
112 
113             void handleDecl(TreePath typePath, Type type) {
114                 Element decl = trees.getElement(typePath.getParentPath());
115                 TestType testType = decl.getAnnotation(TestType.class);
116 
117                 if (testType == null) return ;
118 
119                 if (testType.nested() >= 0) {
120                     ClassType ct = (ClassType) type;
121                     type = ct.getTypeArguments().get(testType.nested());
122                 }
123 
124                 JCExpression typeExpr = TreeMaker.instance(ctx).Type(type);
125 
126                 if (!typeExpr.getKind().equals(testType.expectedKind())) {
127                     throw new IllegalStateException("was=" + typeExpr + ", kind=" +
128                             typeExpr.getKind() + "; expected kind: " +
129                             testType.expectedKind() + "; type=" + type);
130                 }
131                 unseenTypeKinds.remove(type.getKind());
132             }
133 
134         }.scan(path, null);
135 
136         unseenTypeKinds.removeAll(Arrays.asList(TypeKind.NONE, TypeKind.NULL, TypeKind.ERROR,
137                 TypeKind.PACKAGE, TypeKind.EXECUTABLE, TypeKind.OTHER, TypeKind.MODULE));
138 
139         if (!unseenTypeKinds.isEmpty())
140             throw new IllegalStateException("Unhandled types=" + unseenTypeKinds);
141 
142         System.err.println("done.");
143     }
144 
145     //the following defines the Types that should be passed into TreeMaker.Type and
146     //the expected resulting Tree kind:
147 
148     @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE)
149     boolean f1;
150     @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE)
151     byte f2;
152     @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE)
153     char f3;
154     @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE)
155     double f4;
156     @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE)
157     float f5;
158     @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE)
159     int f6;
160     @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE)
161     long f7;
162     @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE)
163     short f8;
164     @TestType(expectedKind=Tree.Kind.PARAMETERIZED_TYPE)
165     List<? extends String> f9;
166     @TestType(expectedKind=Tree.Kind.ARRAY_TYPE)
167     int[] fa;
168     @TestType(expectedKind=Tree.Kind.EXTENDS_WILDCARD, nested = 0)
169     List<? extends String> fb;
170     @TestType(expectedKind=Tree.Kind.SUPER_WILDCARD, nested = 0)
171     List<? super String> fc;
172     @TestType(expectedKind=Tree.Kind.UNBOUNDED_WILDCARD, nested = 0)
173     List<?> fd;
174 
175     @TestType(expectedKind=Tree.Kind.PRIMITIVE_TYPE)
176     void voidMethod() {
177         try {
178             voidMethod();
179         } catch (@TestType(expectedKind=Tree.Kind.UNION_TYPE) NullPointerException |
180                                                               IllegalStateException ex) {
181         }
182     }
183 
184     class WithTypeParam<@TestType(expectedKind=Tree.Kind.INTERSECTION_TYPE)
185                         T extends CharSequence & Runnable> {
186         @TestType(expectedKind=Tree.Kind.IDENTIFIER)
187         T voidMethod() {
188             return null;
189         }
190     }
191 
192 }
193 
194 //TreeMaker.Type will be tested for the type the element annotated by this annotation
195 @Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD,
196          ElementType.PARAMETER, ElementType.TYPE_PARAMETER})
197 @interface TestType {
198     //the expected Tree kind of the Tree that will be returned from TreeMaker.Type for the type
199     public Tree.Kind expectedKind();
200     //if >=0, the current type will be interpreted as a ClassType and the type to test will be
201     //the given type argument:
202     public int nested() default -1;
203 }