NoPrivateTypesExported.java revision 3193:3b3bea483542
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        ));
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
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
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
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
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
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 protected Void defaultAction(Object o, Void p) {
210                error("Unexpected AnnotationValue: " + o.toString());
211                return super.defaultAction(o, p);
212            }
213        }, null);
214    }
215
216    private void verifyTypesAcceptable(Iterable<? extends TypeMirror> types,
217                                       Set<String> acceptable) {
218        if (types == null) return ;
219
220        for (TypeMirror type : types) {
221            verifyTypeAcceptable(type, acceptable);
222        }
223    }
224
225    private void verifyTypeAcceptable(TypeMirror type, Set<String> acceptable) {
226        if (type == null) return ;
227
228        verifyAnnotations(type.getAnnotationMirrors(), acceptable);
229
230        switch (type.getKind()) {
231            case BOOLEAN: case BYTE: case CHAR: case DOUBLE: case FLOAT:
232            case INT: case LONG: case SHORT: case VOID: case NONE: case NULL:
233                return ;
234            case DECLARED:
235                DeclaredType dt = (DeclaredType) type;
236                TypeElement outermostTypeElement = outermostTypeElement(dt.asElement());
237                String outermostType = outermostTypeElement.getQualifiedName().toString();
238                boolean isAcceptable = false;
239                for (String acceptablePackage : acceptable) {
240                    if (outermostType.startsWith(acceptablePackage)) {
241                        isAcceptable = true;
242                        break;
243                    }
244                }
245                if (!isAcceptable) {
246                    error("Type not acceptable for this API: " + dt.toString());
247                }
248
249                for (TypeMirror bound : dt.getTypeArguments()) {
250                    verifyTypeAcceptable(bound, acceptable);
251                }
252                break;
253            case ARRAY:
254                verifyTypeAcceptable(((ArrayType) type).getComponentType(), acceptable);
255                break;
256            case INTERSECTION:
257                for (TypeMirror element : ((IntersectionType) type).getBounds()) {
258                    verifyTypeAcceptable(element, acceptable);
259                }
260                break;
261            case TYPEVAR:
262                verifyTypeAcceptable(((TypeVariable) type).getLowerBound(), acceptable);
263                verifyTypeAcceptable(((TypeVariable) type).getUpperBound(), acceptable);
264                break;
265            case WILDCARD:
266                verifyTypeAcceptable(((WildcardType) type).getExtendsBound(), acceptable);
267                verifyTypeAcceptable(((WildcardType) type).getSuperBound(), acceptable);
268                break;
269            default:
270                error("Type not acceptable for this API: " + type.toString());
271                break;
272
273        }
274    }
275
276    private TypeElement outermostTypeElement(Element el) {
277        while (el.getEnclosingElement().getKind() != ElementKind.PACKAGE) {
278            el = el.getEnclosingElement();
279        }
280
281        return (TypeElement) el;
282    }
283
284    private void error(String text) {
285        processingEnv.getMessager().printMessage(Kind.ERROR, text);
286    }
287
288    private void note(String text) {
289        processingEnv.getMessager().printMessage(Kind.NOTE, text);
290    }
291}
292