1 /*
2  * Copyright (c) 2013, 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 8026180 8132096
27  * @summary Ensuring javax.lang.model.**, javax.tools.**, javax.annotation.processing.**
28  *          and com.sun.source.** don't export inappropriate types.
29  * @library /tools/javac/lib
30  * @modules java.compiler
31  *          jdk.compiler
32  * @build JavacTestingAbstractProcessor NoPrivateTypesExported
33  * @compile -processor NoPrivateTypesExported NoPrivateTypesExported.java
34  */
35 import java.lang.annotation.Documented;
36 import java.util.Arrays;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Set;
40 import javax.annotation.processing.RoundEnvironment;
41 import javax.lang.model.element.AnnotationMirror;
42 import javax.lang.model.element.AnnotationValue;
43 import javax.lang.model.element.Element;
44 import javax.lang.model.element.ElementKind;
45 import javax.lang.model.element.ExecutableElement;
46 import javax.lang.model.element.PackageElement;
47 import javax.lang.model.element.TypeElement;
48 import javax.lang.model.element.TypeParameterElement;
49 import javax.lang.model.element.VariableElement;
50 import javax.lang.model.type.ArrayType;
51 import javax.lang.model.type.DeclaredType;
52 import javax.lang.model.type.IntersectionType;
53 import javax.lang.model.type.TypeMirror;
54 import javax.lang.model.type.TypeVariable;
55 import javax.lang.model.type.WildcardType;
56 import javax.tools.Diagnostic.Kind;
57 
58 public class NoPrivateTypesExported extends JavacTestingAbstractProcessor {
59 
60     private static final String[] javaxLangModelPackages = new String[] {
61         "javax.lang.model",
62         "javax.lang.model.element",
63         "javax.lang.model.type",
64         "javax.lang.model.util",
65     };
66 
67     private static final Set<String> javaxLangModelAcceptable;
68 
69     private static final String[] javaxToolsProcessingPackages = new String[] {
70         "javax.annotation.processing",
71         "javax.tools",
72     };
73 
74     private static final Set<String> javaxToolsProcessingAcceptable;
75 
76     private static final String[] comSunSourcePackages = new String[] {
77         "com.sun.source.doctree",
78         "com.sun.source.tree",
79         "com.sun.source.util"
80     };
81 
82     private static final Set<String> comSunSourceAcceptable;
83 
84     static {
85         javaxLangModelAcceptable = new HashSet<>(Arrays.asList(
86             "java.io.",
87             "java.lang.",
88             "java.net.",
89             "java.nio.",
90             "java.text.",
91             "java.util.",
92             "javax.lang.model.",
93             "javax.annotation.processing.SupportedSourceVersion"
94         ));
95         Set<String> javaxToolsProcessingAcceptableTemp = new HashSet<>();
96         javaxToolsProcessingAcceptableTemp.addAll(javaxLangModelAcceptable);
97         javaxToolsProcessingAcceptableTemp.addAll(Arrays.asList(
98                 "javax.annotation.processing.",
99                 "javax.tools."
100         ));
101         javaxToolsProcessingAcceptable = javaxToolsProcessingAcceptableTemp;
102         Set<String> comSunSourceAcceptableTemp = new HashSet<>();
103         comSunSourceAcceptableTemp.addAll(javaxToolsProcessingAcceptable);
104         comSunSourceAcceptableTemp.addAll(Arrays.asList(
105                 "com.sun.source.doctree.",
106                 "com.sun.source.tree.",
107                 "com.sun.source.util."
108         ));
109         comSunSourceAcceptable = comSunSourceAcceptableTemp;
110     }
111 
112     @Override
process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)113     public boolean process(Set<? extends TypeElement> annotations,
114                            RoundEnvironment roundEnv) {
115         if (roundEnv.processingOver()) {
116             verifyPackage(javaxLangModelPackages, javaxLangModelAcceptable);
117             verifyPackage(javaxToolsProcessingPackages, javaxToolsProcessingAcceptable);
118             verifyPackage(comSunSourcePackages, comSunSourceAcceptable);
119         }
120         return true;
121     }
122 
verifyPackage(String[] packagesToTest, Set<String> acceptable)123     private void verifyPackage(String[] packagesToTest, Set<String> acceptable) {
124         for (String packageToTest : packagesToTest) {
125             PackageElement packageElement = processingEnv.getElementUtils()
126                     .getPackageElement(packageToTest);
127 
128             verifyReferredTypesAcceptable(packageElement, acceptable);
129         }
130     }
131 
verifyReferredTypesAcceptable(Element rootElement, final Set<String> acceptable)132     private void verifyReferredTypesAcceptable(Element rootElement,
133                                                final Set<String> acceptable) {
134         new ElementScanner<Void, Void>() {
135             @Override public Void visitType(TypeElement e, Void p) {
136                 verifyTypeAcceptable(e.getSuperclass(), acceptable);
137                 verifyTypesAcceptable(e.getInterfaces(), acceptable);
138                 scan(e.getTypeParameters(), p);
139                 scan(e.getEnclosedElements(), p);
140                 verifyAnnotations(e.getAnnotationMirrors(), acceptable);
141                 return null;
142             }
143             @Override public Void visitTypeParameter(TypeParameterElement e, Void p) {
144                 verifyTypesAcceptable(e.getBounds(), acceptable);
145                 scan(e.getEnclosedElements(), p);
146                 verifyAnnotations(e.getAnnotationMirrors(), acceptable);
147                 return null;
148             }
149             @Override public Void visitPackage(PackageElement e, Void p) {
150                 scan(e.getEnclosedElements(), p);
151                 verifyAnnotations(e.getAnnotationMirrors(), acceptable);
152                 return null;
153             }
154             @Override public Void visitVariable(VariableElement e, Void p) {
155                 verifyTypeAcceptable(e.asType(), acceptable);
156                 scan(e.getEnclosedElements(), p);
157                 verifyAnnotations(e.getAnnotationMirrors(), acceptable);
158                 return null;
159             }
160             @Override
161             public Void visitExecutable(ExecutableElement e, Void p) {
162                 scan(e.getTypeParameters(), p);
163                 verifyTypeAcceptable(e.getReturnType(), acceptable);
164                 scan(e.getParameters(), p);
165                 verifyTypesAcceptable(e.getThrownTypes(), acceptable);
166                 scan(e.getEnclosedElements(), p);
167                 verifyAnnotations(e.getAnnotationMirrors(), acceptable);
168                 return null;
169             }
170         }.scan(rootElement, null);
171     }
172 
verifyAnnotations(Iterable<? extends AnnotationMirror> annotations, Set<String> acceptable)173     private void verifyAnnotations(Iterable<? extends AnnotationMirror> annotations,
174                                    Set<String> acceptable) {
175         for (AnnotationMirror mirror : annotations) {
176             Element annotationElement = mirror.getAnnotationType().asElement();
177 
178             if (annotationElement.getAnnotation(Documented.class) == null) {
179                 note("Ignoring undocumented annotation: " + mirror.getAnnotationType());
180                 continue;
181             }
182 
183             verifyTypeAcceptable(mirror.getAnnotationType(), acceptable);
184 
185             for (AnnotationValue value : mirror.getElementValues().values()) {
186                 verifyAnnotationValue(value, acceptable);
187             }
188         }
189     }
190 
verifyAnnotationValue(AnnotationValue value, final Set<String> acceptable)191     private void verifyAnnotationValue(AnnotationValue value,
192                                        final Set<String> acceptable) {
193         value.accept(new SimpleAnnotationValueVisitor<Void, Void>() {
194             @Override public Void visitType(TypeMirror t, Void p) {
195                 verifyTypeAcceptable(t, acceptable);
196                 return null;
197             }
198             @Override
199             public Void visitEnumConstant(VariableElement c, Void p) {
200                 verifyReferredTypesAcceptable(c, acceptable);
201                 return null;
202             }
203             @Override public Void visitArray(List<? extends AnnotationValue> vals,
204                                              Void p) {
205                 for (AnnotationValue val : vals) {
206                     val.accept(this, p);
207                 }
208                 return null;
209             }
210             @Override public Void visitBoolean(boolean b, Void p) {
211                 return null;
212             }
213             @Override public Void visitByte(byte b, Void p) {
214                 return null;
215             }
216             @Override public Void visitChar(char c, Void p) {
217                 return null;
218             }
219             @Override public Void visitDouble(double d, Void p) {
220                 return null;
221             }
222             @Override public Void visitFloat(float f, Void p) {
223                 return null;
224             }
225             @Override public Void visitInt(int i, Void p) {
226                 return null;
227             }
228             @Override public Void visitLong(long i, Void p) {
229                 return null;
230             }
231             @Override public Void visitShort(short s, Void p) {
232                 return null;
233             }
234             @Override public Void visitString(String s, Void p) {
235                 return null;
236             }
237             @Override protected Void defaultAction(Object o, Void p) {
238                 error("Unexpected AnnotationValue: " + o.toString());
239                 return super.defaultAction(o, p);
240             }
241         }, null);
242     }
243 
verifyTypesAcceptable(Iterable<? extends TypeMirror> types, Set<String> acceptable)244     private void verifyTypesAcceptable(Iterable<? extends TypeMirror> types,
245                                        Set<String> acceptable) {
246         if (types == null) return ;
247 
248         for (TypeMirror type : types) {
249             verifyTypeAcceptable(type, acceptable);
250         }
251     }
252 
verifyTypeAcceptable(TypeMirror type, Set<String> acceptable)253     private void verifyTypeAcceptable(TypeMirror type, Set<String> acceptable) {
254         if (type == null) return ;
255 
256         verifyAnnotations(type.getAnnotationMirrors(), acceptable);
257 
258         switch (type.getKind()) {
259             case BOOLEAN: case BYTE: case CHAR: case DOUBLE: case FLOAT:
260             case INT: case LONG: case SHORT: case VOID: case NONE: case NULL:
261                 return ;
262             case DECLARED:
263                 DeclaredType dt = (DeclaredType) type;
264                 TypeElement outermostTypeElement = outermostTypeElement(dt.asElement());
265                 String outermostType = outermostTypeElement.getQualifiedName().toString();
266                 boolean isAcceptable = false;
267                 for (String acceptablePackage : acceptable) {
268                     if (outermostType.startsWith(acceptablePackage)) {
269                         isAcceptable = true;
270                         break;
271                     }
272                 }
273                 if (!isAcceptable) {
274                     error("Type not acceptable for this API: " + dt.toString());
275                 }
276 
277                 for (TypeMirror bound : dt.getTypeArguments()) {
278                     verifyTypeAcceptable(bound, acceptable);
279                 }
280                 break;
281             case ARRAY:
282                 verifyTypeAcceptable(((ArrayType) type).getComponentType(), acceptable);
283                 break;
284             case INTERSECTION:
285                 for (TypeMirror element : ((IntersectionType) type).getBounds()) {
286                     verifyTypeAcceptable(element, acceptable);
287                 }
288                 break;
289             case TYPEVAR:
290                 verifyTypeAcceptable(((TypeVariable) type).getLowerBound(), acceptable);
291                 verifyTypeAcceptable(((TypeVariable) type).getUpperBound(), acceptable);
292                 break;
293             case WILDCARD:
294                 verifyTypeAcceptable(((WildcardType) type).getExtendsBound(), acceptable);
295                 verifyTypeAcceptable(((WildcardType) type).getSuperBound(), acceptable);
296                 break;
297             default:
298                 error("Type not acceptable for this API: " + type.toString());
299                 break;
300 
301         }
302     }
303 
outermostTypeElement(Element el)304     private TypeElement outermostTypeElement(Element el) {
305         while (el.getEnclosingElement().getKind() != ElementKind.PACKAGE) {
306             el = el.getEnclosingElement();
307         }
308 
309         return (TypeElement) el;
310     }
311 
error(String text)312     private void error(String text) {
313         processingEnv.getMessager().printMessage(Kind.ERROR, text);
314     }
315 
note(String text)316     private void note(String text) {
317         processingEnv.getMessager().printMessage(Kind.NOTE, text);
318     }
319 }
320