ExportsUnexported.java revision 3822:d8766c39123a
1/*
2 * Copyright (c) 2016, 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 * @summary tests for module declarations
27 * @library /tools/lib
28 * @modules jdk.compiler/com.sun.tools.javac.api
29 *          jdk.compiler/com.sun.tools.javac.main
30 * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase
31 * @run main ExportsUnexported
32 */
33
34import java.nio.file.Path;
35import java.util.ArrayList;
36import java.util.Arrays;
37import java.util.Collections;
38import java.util.List;
39
40import toolbox.JavacTask;
41import toolbox.Task;
42
43public class ExportsUnexported extends ModuleTestBase {
44
45    public static void main(String... args) throws Exception {
46        ExportsUnexported t = new ExportsUnexported();
47        t.runTests();
48    }
49
50    @Test
51    public void testLocations(Path base) throws Exception {
52        String warningsTest = "package api;\n" +
53                      "import impl.impl.*;\n" +
54                      "@impl.impl^.DocAnn\n" +
55                      "public abstract class Api<T extends impl.impl^.Cls&impl.impl^.Intf> extends impl.impl^.Cls implements impl.impl^.Intf, impl.impl^.NonDocAnn, impl.impl^.DocAnn {\n" +
56                      "    public static <E extends impl.impl^.Cls&impl.impl^.Intf> impl.impl^.Cls m(impl.impl^.Intf i, impl.impl^.Cls c) throws impl.impl^.Exc { return null; }\n" +
57                      "    public static impl.impl^.Cls f;\n" +
58                      "}";
59        String noWarningsTest = "package api;\n" +
60                      "import impl.impl.*;\n" +
61                      "@impl.impl.NonDocAnn\n" +
62                      "public abstract class Api {\n" +
63                      "    private static abstract class I <T extends impl.impl.Cls&impl.impl.Intf> extends impl.impl.Cls implements impl.impl.Intf, impl.impl.NonDocAnn, impl.impl.DocAnn {\n" +
64                      "        public static abstract class II <T extends impl.impl.Cls&impl.impl.Intf> extends impl.impl.Cls implements impl.impl.Intf, impl.impl.NonDocAnn, impl.impl.DocAnn { }\n" +
65                      "        public static <E extends impl.impl.Cls&impl.impl.Intf> impl.impl.Cls m(impl.impl.Intf i, impl.impl.Cls c) throws impl.impl.Exc { return null; }\n" +
66                      "        public static impl.impl.Cls f;\n" +
67                      "    }\n" +
68                      "    private static <E extends impl.impl.Cls&impl.impl.Intf> impl.impl.Cls m(impl.impl.Intf i, impl.impl.Cls c) throws impl.impl.Exc { return null; }\n" +
69                      "    private static impl.impl.Cls f1;\n" +
70                      "    public static void m() { new impl.impl.Cls(); }\n" +
71                      "    public static Object f2 = new impl.impl.Cls();\n" +
72                      "}";
73        for (String genericTest : new String[] {warningsTest, noWarningsTest}) {
74            for (String test : new String[] {genericTest, genericTest.replaceAll("impl\\.impl\\^.([A-Za-z])", "^$1").replaceAll("impl\\.impl\\.([A-Za-z])", "$1")}) {
75                System.err.println("testing: " + test);
76
77                Path src = base.resolve("src");
78                Path src_m1 = src.resolve("m1x");
79                StringBuilder testCode = new StringBuilder();
80                List<String> expectedLog = new ArrayList<>();
81                int line = 1;
82                int col  = 1;
83
84                for (int i = 0; i < test.length(); i++) {
85                    char c = test.charAt(i);
86
87                    if (c == '^') {
88                        StringBuilder typeName = new StringBuilder();
89                        for (int j = i + 1 + (test.charAt(i + 1) == '.' ? 1 : 0);
90                             j < test.length() && Character.isJavaIdentifierPart(test.charAt(j));
91                             j++) {
92                            typeName.append(test.charAt(j));
93                        }
94                        String kindName;
95                        switch (typeName.toString()) {
96                            case "Exc": case "DocAnn": case "NonDocAnn":
97                            case "Cls": kindName = "kindname.class"; break;
98                            case "Intf": kindName = "kindname.interface"; break;
99                            default:
100                                throw new AssertionError(typeName.toString());
101                        }
102                        expectedLog.add("Api.java:" + line + ":" + col
103                                + ": compiler.warn.leaks.not.accessible.unexported: "
104                                + kindName + ", impl.impl." + typeName + ", m1x");
105                        continue;
106                    }
107
108                    if (c == '\n') {
109                        line++;
110                        col = 0;
111                    }
112
113                    testCode.append(c);
114                    col++;
115                }
116
117                if (!expectedLog.isEmpty()) {
118                    expectedLog.add("" + expectedLog.size() + " warning" + (expectedLog.size() == 1 ? "" : "s"));
119                    expectedLog.add("- compiler.err.warnings.and.werror");
120                    expectedLog.add("1 error");
121                }
122
123                Collections.sort(expectedLog);
124
125                tb.writeJavaFiles(src_m1,
126                                  "module m1x { exports api; }",
127                                  testCode.toString(),
128                                  "package impl.impl; public class Cls { }",
129                                  "package impl.impl; public class Exc extends Exception { }",
130                                  "package impl.impl; public interface Intf { }",
131                                  "package impl.impl; @java.lang.annotation.Documented public @interface DocAnn { }",
132                                  "package impl.impl; public @interface NonDocAnn { }");
133                Path classes = base.resolve("classes");
134                tb.createDirectories(classes);
135
136                List<String> log = new JavacTask(tb)
137                        .options("-XDrawDiagnostics",
138                                 "-Werror",
139                                 "--module-source-path", src.toString(),
140                                 "-Xlint:exports")
141                        .outdir(classes)
142                        .files(findJavaFiles(src))
143                        .run(expectedLog.isEmpty() ? Task.Expect.SUCCESS : Task.Expect.FAIL)
144                        .writeAll()
145                        .getOutputLines(Task.OutputKind.DIRECT);
146
147                log = new ArrayList<>(log);
148                Collections.sort(log);
149
150                if (expectedLog.isEmpty() ? !log.equals(Arrays.asList("")) : !log.equals(expectedLog)) {
151                    throw new Exception("expected output not found in: " + log + "; " + expectedLog);
152                }
153            }
154        }
155    }
156
157    @Test
158    public void testAccessibleToSpecificOrAll(Path base) throws Exception {
159        Path src = base.resolve("src");
160        Path src_lib1 = src.resolve("lib1x");
161        tb.writeJavaFiles(src_lib1,
162                          "module lib1x { exports lib1; }",
163                          "package lib1; public class Lib1 {}");
164        Path src_lib2 = src.resolve("lib2x");
165        tb.writeJavaFiles(src_lib2,
166                          "module lib2x { exports lib2; }",
167                          "package lib2; public class Lib2 {}");
168        Path src_api = src.resolve("api");
169        tb.writeJavaFiles(src_api,
170                          "module api {\n" +
171                          "    exports api;\n" +
172                          "    exports qapi1 to qual1x;\n" +
173                          "    exports qapi2 to qual1x, qual2x;\n" +
174                          "    requires transitive lib1x;\n" +
175                          "    requires lib2x;\n" +
176                          "}\n",
177                          "package api;\n" +
178                          "public class Api {\n" +
179                          "    public lib1.Lib1 lib1;\n" +
180                          "    public lib2.Lib2 lib2;\n" +
181                          "    public qapi1.QApi1 qapi1;\n" +
182                          "    public impl.Impl impl;\n" +
183                          "}",
184                          "package qapi1;\n" +
185                          "public class QApi1 {\n" +
186                          "    public qapi2.QApi2 qapi2;\n" +
187                          "}",
188                          "package qapi2;\n" +
189                          "public class QApi2 {\n" +
190                          "    public qapi1.QApi1 qapi1;\n" +
191                          "}",
192                          "package impl;\n" +
193                          "public class Impl {\n" +
194                          "}");
195        Path src_qual1 = src.resolve("qual1x");
196        tb.writeJavaFiles(src_qual1, "module qual1x { }");
197        Path src_qual2 = src.resolve("qual2x");
198        tb.writeJavaFiles(src_qual2, "module qual2x { }");
199        Path classes = base.resolve("classes");
200        tb.createDirectories(classes);
201
202        List<String> log = new JavacTask(tb)
203                .options("-XDrawDiagnostics",
204                         "-Werror",
205                         "--module-source-path", src.toString(),
206                         "-Xlint:exports")
207                .outdir(classes)
208                .files(findJavaFiles(src))
209                .run(Task.Expect.FAIL)
210                .writeAll()
211                .getOutputLines(Task.OutputKind.DIRECT);
212
213        List<String> expected = Arrays.asList(
214            "Api.java:4:16: compiler.warn.leaks.not.accessible.not.required.transitive: kindname.class, lib2.Lib2, lib2x",
215            "Api.java:5:17: compiler.warn.leaks.not.accessible.unexported.qualified: kindname.class, qapi1.QApi1, api",
216            "Api.java:6:16: compiler.warn.leaks.not.accessible.unexported: kindname.class, impl.Impl, api",
217            "- compiler.err.warnings.and.werror",
218            "1 error",
219            "3 warnings"
220        );
221
222        if (!log.equals(expected))
223            throw new Exception("expected output not found");
224    }
225
226    @Test
227    public void testNestedClasses(Path base) throws Exception {
228        Path src = base.resolve("src");
229        Path src_api = src.resolve("api");
230        tb.writeJavaFiles(src_api,
231                          "module api {\n" +
232                          "    exports api;\n" +
233                          "}\n",
234                          "package api;\n" +
235                          "import impl.Impl.Nested;\n" +
236                          "public class Api {\n" +
237                          "    public impl.Impl impl1;\n" +
238                          "    public impl.Impl.Nested impl2;\n" +
239                          "    public Nested impl3;\n" +
240                          "}",
241                          "package impl;\n" +
242                          "public class Impl {\n" +
243                          "    public static class Nested {\n" +
244                          "    }\n" +
245                          "}");
246        Path classes = base.resolve("classes");
247        tb.createDirectories(classes);
248
249        List<String> log = new JavacTask(tb)
250                .options("-XDrawDiagnostics",
251                         "-Werror",
252                         "--module-source-path", src.toString(),
253                         "-Xlint:exports")
254                .outdir(classes)
255                .files(findJavaFiles(src))
256                .run(Task.Expect.FAIL)
257                .writeAll()
258                .getOutputLines(Task.OutputKind.DIRECT);
259
260        List<String> expected = Arrays.asList(
261            "Api.java:4:16: compiler.warn.leaks.not.accessible.unexported: kindname.class, impl.Impl, api",
262            "Api.java:5:16: compiler.warn.leaks.not.accessible.unexported: kindname.class, impl.Impl, api",
263            "Api.java:6:12: compiler.warn.leaks.not.accessible.unexported: kindname.class, impl.Impl.Nested, api",
264            "- compiler.err.warnings.and.werror",
265            "1 error",
266            "3 warnings"
267        );
268
269        if (!log.equals(expected))
270            throw new Exception("expected output not found");
271    }
272
273    @Test
274    public void testProtectedAndInaccessible(Path base) throws Exception {
275        Path src = base.resolve("src");
276        Path src_api = src.resolve("api");
277        tb.writeJavaFiles(src_api,
278                          "module api {\n" +
279                          "    exports api;\n" +
280                          "}\n",
281                          "package api;\n" +
282                          "public class Api extends PackagePrivateClass<PackagePrivateInterface> implements PackagePrivateInterface<PackagePrivateClass> {\n" +
283                          "    protected PackagePrivateClass<?> f1;\n" +
284                          "    protected PackagePrivateInterface<?> f2;\n" +
285                          "    protected Inner f3;\n" +
286                          "    protected PrivateInner f4;\n" +
287                          "    protected impl.Impl f5;\n" +
288                          "    public static class InnerClass extends PrivateInner {}\n" +
289                          "    protected static class Inner {}\n" +
290                          "    private static class PrivateInner {}\n" +
291                          "}\n" +
292                          "class PackagePrivateClass<T> {}\n" +
293                          "interface PackagePrivateInterface<T> {}",
294                          "package impl;\n" +
295                          "public class Impl {\n" +
296                          "}");
297        Path classes = base.resolve("classes");
298        tb.createDirectories(classes);
299
300        List<String> log = new JavacTask(tb)
301                .options("-XDrawDiagnostics",
302                         "-Werror",
303                         "--module-source-path", src.toString(),
304                         "-Xlint:exports")
305                .outdir(classes)
306                .files(findJavaFiles(src))
307                .run(Task.Expect.FAIL)
308                .writeAll()
309                .getOutputLines(Task.OutputKind.DIRECT);
310
311        List<String> expected = Arrays.asList(
312            "Api.java:2:46: compiler.warn.leaks.not.accessible: kindname.interface, api.PackagePrivateInterface, api",
313            "Api.java:2:106: compiler.warn.leaks.not.accessible: kindname.class, api.PackagePrivateClass, api",
314            "Api.java:3:15: compiler.warn.leaks.not.accessible: kindname.class, api.PackagePrivateClass, api",
315            "Api.java:4:15: compiler.warn.leaks.not.accessible: kindname.interface, api.PackagePrivateInterface, api",
316            "Api.java:6:15: compiler.warn.leaks.not.accessible: kindname.class, api.Api.PrivateInner, api",
317            "Api.java:7:19: compiler.warn.leaks.not.accessible.unexported: kindname.class, impl.Impl, api",
318            "- compiler.err.warnings.and.werror",
319            "1 error",
320            "6 warnings"
321        );
322
323        if (!log.equals(expected))
324            throw new Exception("expected output not found");
325    }
326
327    @Test
328    public void testSuppressResetProperly(Path base) throws Exception {
329        Path src = base.resolve("src");
330        Path src_api = src.resolve("api");
331        tb.writeJavaFiles(src_api,
332                          "module api {\n" +
333                          "    exports api;\n" +
334                          "}\n",
335                          "package api;\n" +
336                          "public class Api {\n" +
337                          "    @SuppressWarnings(\"exports\")\n" +
338                          "    public PackagePrivateClass f1;\n" +
339                          "    public PackagePrivateClass f2;\n" +
340                          "    @SuppressWarnings(\"exports\")\n" +
341                          "    public void t() {}\n" +
342                          "    public PackagePrivateClass f3;\n" +
343                          "    @SuppressWarnings(\"exports\")\n" +
344                          "    public static class C {\n" +
345                          "        public PackagePrivateClass f4;\n" +
346                          "    }\n" +
347                          "    public PackagePrivateClass f5;\n" +
348                          "}\n" +
349                          "class PackagePrivateClass<T> {}\n");
350        Path classes = base.resolve("classes");
351        tb.createDirectories(classes);
352
353        List<String> log = new JavacTask(tb)
354                .options("-XDrawDiagnostics",
355                         "-Werror",
356                         "--module-source-path", src.toString(),
357                         "-Xlint:exports")
358                .outdir(classes)
359                .files(findJavaFiles(src))
360                .run(Task.Expect.FAIL)
361                .writeAll()
362                .getOutputLines(Task.OutputKind.DIRECT);
363
364        List<String> expected = Arrays.asList(
365            "Api.java:5:12: compiler.warn.leaks.not.accessible: kindname.class, api.PackagePrivateClass, api",
366            "Api.java:8:12: compiler.warn.leaks.not.accessible: kindname.class, api.PackagePrivateClass, api",
367            "Api.java:13:12: compiler.warn.leaks.not.accessible: kindname.class, api.PackagePrivateClass, api",
368            "- compiler.err.warnings.and.werror",
369            "1 error",
370            "3 warnings"
371        );
372
373        if (!log.equals(expected))
374            throw new Exception("expected output not found");
375    }
376
377}
378