NoPrivateTypesExported.java revision 3074:522e516b8a83
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 */
35import java.lang.annotation.Documented;
36import java.util.Arrays;
37import java.util.HashSet;
38import java.util.List;
39import java.util.Set;
40import javax.annotation.processing.RoundEnvironment;
41import javax.lang.model.element.AnnotationMirror;
42import javax.lang.model.element.AnnotationValue;
43import javax.lang.model.element.Element;
44import javax.lang.model.element.ElementKind;
45import javax.lang.model.element.ExecutableElement;
46import javax.lang.model.element.PackageElement;
47import javax.lang.model.element.TypeElement;
48import javax.lang.model.element.TypeParameterElement;
49import javax.lang.model.element.VariableElement;
50import javax.lang.model.type.ArrayType;
51import javax.lang.model.type.DeclaredType;
52import javax.lang.model.type.IntersectionType;
53import javax.lang.model.type.TypeMirror;
54import javax.lang.model.type.TypeVariable;
55import javax.lang.model.type.WildcardType;
56import javax.tools.Diagnostic.Kind;
57
58public 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            "jdk.Exported"
95        ));
96        Set<String> javaxToolsProcessingAcceptableTemp = new HashSet<>();
97        javaxToolsProcessingAcceptableTemp.addAll(javaxLangModelAcceptable);
98        javaxToolsProcessingAcceptableTemp.addAll(Arrays.asList(
99                "javax.annotation.processing.",
100                "javax.tools."
101        ));
102        javaxToolsProcessingAcceptable = javaxToolsProcessingAcceptableTemp;
103        Set<String> comSunSourceAcceptableTemp = new HashSet<>();
104        comSunSourceAcceptableTemp.addAll(javaxToolsProcessingAcceptable);
105        comSunSourceAcceptableTemp.addAll(Arrays.asList(
106                "com.sun.source.doctree.",
107                "com.sun.source.tree.",
108                "com.sun.source.util."
109        ));
110        comSunSourceAcceptable = comSunSourceAcceptableTemp;
111    }
112
113    @Override
114    public boolean process(Set<? extends TypeElement> annotations,
115                           RoundEnvironment roundEnv) {
116        if (roundEnv.processingOver()) {
117            verifyPackage(javaxLangModelPackages, javaxLangModelAcceptable);
118            verifyPackage(javaxToolsProcessingPackages, javaxToolsProcessingAcceptable);
119            verifyPackage(comSunSourcePackages, comSunSourceAcceptable);
120        }
121        return true;
122    }
123
124    private void verifyPackage(String[] packagesToTest, Set<String> acceptable) {
125        for (String packageToTest : packagesToTest) {
126            PackageElement packageElement = processingEnv.getElementUtils()
127                    .getPackageElement(packageToTest);
128
129            verifyReferredTypesAcceptable(packageElement, acceptable);
130        }
131    }
132
133    private void verifyReferredTypesAcceptable(Element rootElement,
134                                               final Set<String> acceptable) {
135        new ElementScanner<Void, Void>() {
136            @Override public Void visitType(TypeElement e, Void p) {
137                verifyTypeAcceptable(e.getSuperclass(), acceptable);
138                verifyTypesAcceptable(e.getInterfaces(), acceptable);
139                scan(e.getTypeParameters(), p);
140                scan(e.getEnclosedElements(), p);
141                verifyAnnotations(e.getAnnotationMirrors(), acceptable);
142                return null;
143            }
144            @Override public Void visitTypeParameter(TypeParameterElement e, Void p) {
145                verifyTypesAcceptable(e.getBounds(), acceptable);
146                scan(e.getEnclosedElements(), p);
147                verifyAnnotations(e.getAnnotationMirrors(), acceptable);
148                return null;
149            }
150            @Override public Void visitPackage(PackageElement e, Void p) {
151                scan(e.getEnclosedElements(), p);
152                verifyAnnotations(e.getAnnotationMirrors(), acceptable);
153                return null;
154            }
155            @Override public Void visitVariable(VariableElement e, Void p) {
156                verifyTypeAcceptable(e.asType(), acceptable);
157                scan(e.getEnclosedElements(), p);
158                verifyAnnotations(e.getAnnotationMirrors(), acceptable);
159                return null;
160            }
161            @Override
162            public Void visitExecutable(ExecutableElement e, Void p) {
163                scan(e.getTypeParameters(), p);
164                verifyTypeAcceptable(e.getReturnType(), acceptable);
165                scan(e.getParameters(), p);
166                verifyTypesAcceptable(e.getThrownTypes(), acceptable);
167                scan(e.getEnclosedElements(), p);
168                verifyAnnotations(e.getAnnotationMirrors(), acceptable);
169                return null;
170            }
171        }.scan(rootElement, null);
172    }
173
174    private void verifyAnnotations(Iterable<? extends AnnotationMirror> annotations,
175                                   Set<String> acceptable) {
176        for (AnnotationMirror mirror : annotations) {
177            Element annotationElement = mirror.getAnnotationType().asElement();
178
179            if (annotationElement.getAnnotation(Documented.class) == null) {
180                note("Ignoring undocumented annotation: " + mirror.getAnnotationType());
181            }
182
183            verifyTypeAcceptable(mirror.getAnnotationType(), acceptable);
184
185            for (AnnotationValue value : mirror.getElementValues().values()) {
186                verifyAnnotationValue(value, acceptable);
187            }
188        }
189    }
190
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 protected Void defaultAction(Object o, Void p) {
211                error("Unexpected AnnotationValue: " + o.toString());
212                return super.defaultAction(o, p);
213            }
214        }, null);
215    }
216
217    private void verifyTypesAcceptable(Iterable<? extends TypeMirror> types,
218                                       Set<String> acceptable) {
219        if (types == null) return ;
220
221        for (TypeMirror type : types) {
222            verifyTypeAcceptable(type, acceptable);
223        }
224    }
225
226    private void verifyTypeAcceptable(TypeMirror type, Set<String> acceptable) {
227        if (type == null) return ;
228
229        verifyAnnotations(type.getAnnotationMirrors(), acceptable);
230
231        switch (type.getKind()) {
232            case BOOLEAN: case BYTE: case CHAR: case DOUBLE: case FLOAT:
233            case INT: case LONG: case SHORT: case VOID: case NONE: case NULL:
234                return ;
235            case DECLARED:
236                DeclaredType dt = (DeclaredType) type;
237                TypeElement outermostTypeElement = outermostTypeElement(dt.asElement());
238                String outermostType = outermostTypeElement.getQualifiedName().toString();
239                boolean isAcceptable = false;
240                for (String acceptablePackage : acceptable) {
241                    if (outermostType.startsWith(acceptablePackage)) {
242                        isAcceptable = true;
243                        break;
244                    }
245                }
246                if (!isAcceptable) {
247                    error("Type not acceptable for this API: " + dt.toString());
248                }
249
250                for (TypeMirror bound : dt.getTypeArguments()) {
251                    verifyTypeAcceptable(bound, acceptable);
252                }
253                break;
254            case ARRAY:
255                verifyTypeAcceptable(((ArrayType) type).getComponentType(), acceptable);
256                break;
257            case INTERSECTION:
258                for (TypeMirror element : ((IntersectionType) type).getBounds()) {
259                    verifyTypeAcceptable(element, acceptable);
260                }
261                break;
262            case TYPEVAR:
263                verifyTypeAcceptable(((TypeVariable) type).getLowerBound(), acceptable);
264                verifyTypeAcceptable(((TypeVariable) type).getUpperBound(), acceptable);
265                break;
266            case WILDCARD:
267                verifyTypeAcceptable(((WildcardType) type).getExtendsBound(), acceptable);
268                verifyTypeAcceptable(((WildcardType) type).getSuperBound(), acceptable);
269                break;
270            default:
271                error("Type not acceptable for this API: " + type.toString());
272                break;
273
274        }
275    }
276
277    private TypeElement outermostTypeElement(Element el) {
278        while (el.getEnclosingElement().getKind() != ElementKind.PACKAGE) {
279            el = el.getEnclosingElement();
280        }
281
282        return (TypeElement) el;
283    }
284
285    private void error(String text) {
286        processingEnv.getMessager().printMessage(Kind.ERROR, text);
287    }
288
289    private void note(String text) {
290        processingEnv.getMessager().printMessage(Kind.NOTE, text);
291    }
292}
293