8042239: javac: TreeMaker.Type(Type t) does not handle UnionClassType
authorjlahoda
Tue, 08 Jul 2014 15:13:16 +0200
changeset 25440 7f9cedf2e88d
parent 25439 26d6d07eebc7
child 25441 92dcee5a4741
8042239: javac: TreeMaker.Type(Type t) does not handle UnionClassType Summary: Enhancing TreeMaker.Type(Type t) with support for UnionClassType and IntersectionClassType. Reviewed-by: vromero, rfield Contributed-by: robert.field@oracle.com, jan.lahoda@oracle.com
langtools/src/share/classes/com/sun/tools/javac/code/Type.java
langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java
langtools/test/tools/javac/tree/MakeTypeTest.java
langtools/test/tools/javac/tree/MakeTypeTest.out
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Type.java	Mon Jul 07 13:39:31 2014 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Type.java	Tue Jul 08 15:13:16 2014 +0200
@@ -1050,6 +1050,10 @@
         public <R, P> R accept(TypeVisitor<R, P> v, P p) {
             return v.visitUnion(this, p);
         }
+
+        public Iterable<? extends Type> getAlternativeTypes() {
+            return alternatives_field;
+        }
     }
 
     // a clone of a ClassType that knows about the bounds of an intersection type.
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Mon Jul 07 13:39:31 2014 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Tue Jul 08 15:13:16 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2014, 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
@@ -692,13 +692,36 @@
             break;
         }
         case CLASS:
-            Type outer = t.getEnclosingType();
-            JCExpression clazz = outer.hasTag(CLASS) && t.tsym.owner.kind == TYP
-                ? Select(Type(outer), t.tsym)
-                : QualIdent(t.tsym);
-            tp = t.getTypeArguments().isEmpty()
-                ? clazz
-                : TypeApply(clazz, Types(t.getTypeArguments()));
+            switch (t.getKind()) {
+            case UNION: {
+                UnionClassType tu = (UnionClassType)t;
+                ListBuffer<JCExpression> la = new ListBuffer<>();
+                for (Type ta : tu.getAlternativeTypes()) {
+                    la.add(Type(ta));
+                }
+                tp = TypeUnion(la.toList());
+                break;
+            }
+            case INTERSECTION: {
+                IntersectionClassType it = (IntersectionClassType)t;
+                ListBuffer<JCExpression> la = new ListBuffer<>();
+                for (Type ta : it.getExplicitComponents()) {
+                    la.add(Type(ta));
+                }
+                tp = TypeIntersection(la.toList());
+                break;
+            }
+            default: {
+                Type outer = t.getEnclosingType();
+                JCExpression clazz = outer.hasTag(CLASS) && t.tsym.owner.kind == TYP
+                        ? Select(Type(outer), t.tsym)
+                        : QualIdent(t.tsym);
+                tp = t.getTypeArguments().isEmpty()
+                        ? clazz
+                        : TypeApply(clazz, Types(t.getTypeArguments()));
+                break;
+            }
+            }
             break;
         case ARRAY:
             tp = TypeArray(Type(types.elemtype(t)));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/tree/MakeTypeTest.java	Tue Jul 08 15:13:16 2014 +0200
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2014, 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
+ * @build JavacTestingAbstractProcessor MakeTypeTest
+ * @compile/process/ref=MakeTypeTest.out -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));
+
+        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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/tree/MakeTypeTest.out	Tue Jul 08 15:13:16 2014 +0200
@@ -0,0 +1,1 @@
+done.