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             }
181 
182             verifyTypeAcceptable(mirror.getAnnotationType(), acceptable);
183 
184             for (AnnotationValue value : mirror.getElementValues().values()) {
185                 verifyAnnotationValue(value, acceptable);
186             }
187         }
188     }
189 
verifyAnnotationValue(AnnotationValue value, final Set<String> acceptable)190     private void verifyAnnotationValue(AnnotationValue value,
191                                        final Set<String> acceptable) {
192         value.accept(new SimpleAnnotationValueVisitor<Void, Void>() {
193             @Override public Void visitType(TypeMirror t, Void p) {
194                 verifyTypeAcceptable(t, acceptable);
195                 return null;
196             }
197             @Override
198             public Void visitEnumConstant(VariableElement c, Void p) {
199                 verifyReferredTypesAcceptable(c, acceptable);
200                 return null;
201             }
202             @Override public Void visitArray(List<? extends AnnotationValue> vals,
203                                              Void p) {
204                 for (AnnotationValue val : vals) {
205                     val.accept(this, p);
206                 }
207                 return null;
208             }
209             @Override public Void visitBoolean(boolean b, Void p) {
210                 return null;
211             }
212             @Override public Void visitByte(byte b, Void p) {
213                 return null;
214             }
215             @Override public Void visitChar(char c, Void p) {
216                 return null;
217             }
218             @Override public Void visitDouble(double d, Void p) {
219                 return null;
220             }
221             @Override public Void visitFloat(float f, Void p) {
222                 return null;
223             }
224             @Override public Void visitInt(int i, Void p) {
225                 return null;
226             }
227             @Override public Void visitLong(long i, Void p) {
228                 return null;
229             }
230             @Override public Void visitShort(short s, Void p) {
231                 return null;
232             }
233             @Override public Void visitString(String s, Void p) {
234                 return null;
235             }
236             @Override protected Void defaultAction(Object o, Void p) {
237                 error("Unexpected AnnotationValue: " + o.toString());
238                 return super.defaultAction(o, p);
239             }
240         }, null);
241     }
242 
verifyTypesAcceptable(Iterable<? extends TypeMirror> types, Set<String> acceptable)243     private void verifyTypesAcceptable(Iterable<? extends TypeMirror> types,
244                                        Set<String> acceptable) {
245         if (types == null) return ;
246 
247         for (TypeMirror type : types) {
248             verifyTypeAcceptable(type, acceptable);
249         }
250     }
251 
verifyTypeAcceptable(TypeMirror type, Set<String> acceptable)252     private void verifyTypeAcceptable(TypeMirror type, Set<String> acceptable) {
253         if (type == null) return ;
254 
255         verifyAnnotations(type.getAnnotationMirrors(), acceptable);
256 
257         switch (type.getKind()) {
258             case BOOLEAN: case BYTE: case CHAR: case DOUBLE: case FLOAT:
259             case INT: case LONG: case SHORT: case VOID: case NONE: case NULL:
260                 return ;
261             case DECLARED:
262                 DeclaredType dt = (DeclaredType) type;
263                 TypeElement outermostTypeElement = outermostTypeElement(dt.asElement());
264                 String outermostType = outermostTypeElement.getQualifiedName().toString();
265                 boolean isAcceptable = false;
266                 for (String acceptablePackage : acceptable) {
267                     if (outermostType.startsWith(acceptablePackage)) {
268                         isAcceptable = true;
269                         break;
270                     }
271                 }
272                 if (!isAcceptable) {
273                     error("Type not acceptable for this API: " + dt.toString());
274                 }
275 
276                 for (TypeMirror bound : dt.getTypeArguments()) {
277                     verifyTypeAcceptable(bound, acceptable);
278                 }
279                 break;
280             case ARRAY:
281                 verifyTypeAcceptable(((ArrayType) type).getComponentType(), acceptable);
282                 break;
283             case INTERSECTION:
284                 for (TypeMirror element : ((IntersectionType) type).getBounds()) {
285                     verifyTypeAcceptable(element, acceptable);
286                 }
287                 break;
288             case TYPEVAR:
289                 verifyTypeAcceptable(((TypeVariable) type).getLowerBound(), acceptable);
290                 verifyTypeAcceptable(((TypeVariable) type).getUpperBound(), acceptable);
291                 break;
292             case WILDCARD:
293                 verifyTypeAcceptable(((WildcardType) type).getExtendsBound(), acceptable);
294                 verifyTypeAcceptable(((WildcardType) type).getSuperBound(), acceptable);
295                 break;
296             default:
297                 error("Type not acceptable for this API: " + type.toString());
298                 break;
299 
300         }
301     }
302 
outermostTypeElement(Element el)303     private TypeElement outermostTypeElement(Element el) {
304         while (el.getEnclosingElement().getKind() != ElementKind.PACKAGE) {
305             el = el.getEnclosingElement();
306         }
307 
308         return (TypeElement) el;
309     }
310 
error(String text)311     private void error(String text) {
312         processingEnv.getMessager().printMessage(Kind.ERROR, text);
313     }
314 
note(String text)315     private void note(String text) {
316         processingEnv.getMessager().printMessage(Kind.NOTE, text);
317     }
318 }
319