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 }