8178011: Automatic module warnings
authorjlahoda
Mon, 10 Apr 2017 11:08:59 +0200
changeset 44576 9e18c9ce29e7
parent 44575 e8892d76055f
child 44577 3965b747cfe1
child 44618 4ca00828fc0f
child 44682 c8ca44d9888b
8178011: Automatic module warnings Summary: Adding lints for automatic modules in requires and requires transitive directives. Reviewed-by: jjg
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties
langtools/test/tools/javac/diags/Example.java
langtools/test/tools/javac/diags/examples/RequiresAutomatic/module-info.java
langtools/test/tools/javac/diags/examples/RequiresAutomatic/modulepath/a/A.java
langtools/test/tools/javac/diags/examples/RequiresTransitiveAutomatic/module-info.java
langtools/test/tools/javac/diags/examples/RequiresTransitiveAutomatic/modulepath/a/A.java
langtools/test/tools/javac/modules/AutomaticModules.java
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java	Fri Apr 07 15:46:31 2017 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java	Mon Apr 10 11:08:59 2017 +0200
@@ -118,6 +118,7 @@
             if (source.compareTo(Source.JDK1_9) >= 0) {
                 values.add(LintCategory.DEP_ANN);
             }
+            values.add(LintCategory.REQUIRES_TRANSITIVE_AUTOMATIC);
             values.add(LintCategory.OPENS);
             values.add(LintCategory.MODULE);
             values.add(LintCategory.REMOVAL);
@@ -254,6 +255,16 @@
         REMOVAL("removal"),
 
         /**
+         * Warn about use of automatic modules in the requires clauses.
+         */
+        REQUIRES_AUTOMATIC("requires-automatic"),
+
+        /**
+         * Warn about automatic modules in requires transitive.
+         */
+        REQUIRES_TRANSITIVE_AUTOMATIC("requires-transitive-automatic"),
+
+        /**
          * Warn about Serializable classes that do not provide a serial version ID.
          */
         SERIAL("serial"),
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Fri Apr 07 15:46:31 2017 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Mon Apr 10 11:08:59 2017 +0200
@@ -3908,4 +3908,16 @@
         }
     }
 
+    void checkModuleRequires(final DiagnosticPosition pos, final RequiresDirective rd) {
+        if ((rd.module.flags() & Flags.AUTOMATIC_MODULE) != 0) {
+            deferredLintHandler.report(() -> {
+                if (rd.isTransitive() && lint.isEnabled(LintCategory.REQUIRES_TRANSITIVE_AUTOMATIC)) {
+                    log.warning(pos, Warnings.RequiresTransitiveAutomatic);
+                } else if (lint.isEnabled(LintCategory.REQUIRES_AUTOMATIC)) {
+                    log.warning(pos, Warnings.RequiresAutomatic);
+                }
+            });
+        }
+    }
+
 }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java	Fri Apr 07 15:46:31 2017 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java	Mon Apr 10 11:08:59 2017 +0200
@@ -1084,6 +1084,7 @@
         public void visitRequires(JCRequires tree) {
             if (tree.directive != null && allModules().contains(tree.directive.module)) {
                 chk.checkDeprecated(tree.moduleName.pos(), msym, tree.directive.module);
+                chk.checkModuleRequires(tree.moduleName.pos(), tree.directive);
                 msym.directives = msym.directives.prepend(tree.directive);
             }
         }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Apr 07 15:46:31 2017 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Mon Apr 10 11:08:59 2017 +0200
@@ -1664,6 +1664,12 @@
 compiler.warn.future.attr=\
     {0} attribute introduced in version {1}.{2} class files is ignored in version {3}.{4} class files
 
+compiler.warn.requires.automatic=\
+    requires directive for an automatic module
+
+compiler.warn.requires.transitive.automatic=\
+    requires transitive directive for an automatic module
+
 # Warnings related to annotation processing
 # 0: string
 compiler.warn.proc.package.does.not.exist=\
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties	Fri Apr 07 15:46:31 2017 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties	Mon Apr 10 11:08:59 2017 +0200
@@ -230,6 +230,12 @@
 javac.opt.Xlint.desc.removal=\
     Warn about use of API that has been marked for removal.
 
+javac.opt.Xlint.desc.requires-automatic=\
+    Warn about use of automatic modules in the requires clauses.
+
+javac.opt.Xlint.desc.requires-transitive-automatic=\
+    Warn about automatic modules in requires transitive.
+
 javac.opt.Xlint.desc.serial=\
     Warn about Serializable classes that do not provide a serial version ID. \n\
 \                             Also warn about access to non-public members from a serializable element.
--- a/langtools/test/tools/javac/diags/Example.java	Fri Apr 07 15:46:31 2017 +0100
+++ b/langtools/test/tools/javac/diags/Example.java	Mon Apr 10 11:08:59 2017 +0200
@@ -24,8 +24,18 @@
 import java.io.*;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.*;
+import java.util.Map.Entry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import java.util.regex.*;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+
 import javax.annotation.processing.Processor;
 import javax.tools.Diagnostic;
 import javax.tools.DiagnosticCollector;
@@ -210,9 +220,53 @@
             File modulepathDir = new File(tempDir, "modulepath");
             modulepathDir.mkdirs();
             clean(modulepathDir);
-            List<String> sOpts = Arrays.asList("-d", modulepathDir.getPath(),
-                                               "--module-source-path", new File(file, "modulepath").getAbsolutePath());
-            new Jsr199Compiler(verbose).run(null, null, false, sOpts, modulePathFiles);
+            boolean hasModuleInfo =
+                    modulePathFiles.stream()
+                                   .anyMatch(f -> f.getName().equalsIgnoreCase("module-info.java"));
+            Path modulePath = new File(file, "modulepath").toPath().toAbsolutePath();
+            if (hasModuleInfo) {
+                //ordinary modules
+                List<String> sOpts =
+                        Arrays.asList("-d", modulepathDir.getPath(),
+                                      "--module-source-path", modulePath.toString());
+                new Jsr199Compiler(verbose).run(null, null, false, sOpts, modulePathFiles);
+            } else {
+                //automatic modules:
+                Map<String, List<Path>> module2Files =
+                        modulePathFiles.stream()
+                                       .map(f -> f.toPath())
+                                       .collect(Collectors.groupingBy(p -> modulePath.relativize(p)
+                                                                            .getName(0)
+                                                                            .toString()));
+                for (Entry<String, List<Path>> e : module2Files.entrySet()) {
+                    File scratchDir = new File(tempDir, "scratch");
+                    scratchDir.mkdirs();
+                    clean(scratchDir);
+                    List<String> sOpts =
+                            Arrays.asList("-d", scratchDir.getPath());
+                    new Jsr199Compiler(verbose).run(null,
+                                                    null,
+                                                    false,
+                                                    sOpts,
+                                                    e.getValue().stream()
+                                                                .map(p -> p.toFile())
+                                                                .collect(Collectors.toList()));
+                    try (JarOutputStream jarOut =
+                            new JarOutputStream(new FileOutputStream(new File(modulepathDir, e.getKey() + ".jar")))) {
+                        Files.find(scratchDir.toPath(), Integer.MAX_VALUE, (p, attr) -> attr.isRegularFile())
+                                .forEach(p -> {
+                                    try (InputStream in = Files.newInputStream(p)) {
+                                        jarOut.putNextEntry(new ZipEntry(scratchDir.toPath()
+                                                                                   .relativize(p)
+                                                                                   .toString()));
+                                        jarOut.write(in.readAllBytes());
+                                    } catch (IOException ex) {
+                                        throw new IllegalStateException(ex);
+                                    }
+                                });
+                    }
+                }
+            }
             opts.add("--module-path");
             opts.add(modulepathDir.getAbsolutePath());
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/RequiresAutomatic/module-info.java	Mon Apr 10 11:08:59 2017 +0200
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+//options: -Xlint:requires-automatic
+//key: compiler.warn.requires.automatic
+module RequiresAutomatic {
+    requires a;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/RequiresAutomatic/modulepath/a/A.java	Mon Apr 10 11:08:59 2017 +0200
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public class A {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/RequiresTransitiveAutomatic/module-info.java	Mon Apr 10 11:08:59 2017 +0200
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+//key: compiler.warn.requires.transitive.automatic
+module RequiresTransitiveAutomatic {
+    requires transitive a;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/RequiresTransitiveAutomatic/modulepath/a/A.java	Mon Apr 10 11:08:59 2017 +0200
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public class A {}
--- a/langtools/test/tools/javac/modules/AutomaticModules.java	Fri Apr 07 15:46:31 2017 +0100
+++ b/langtools/test/tools/javac/modules/AutomaticModules.java	Mon Apr 10 11:08:59 2017 +0200
@@ -23,7 +23,7 @@
 
 /**
  * @test
- * @bug 8155026
+ * @bug 8155026 8178011
  * @summary Test automatic modules
  * @library /tools/lib
  * @modules
@@ -423,4 +423,195 @@
                 .run()
                 .writeAll();
     }
+
+    @Test
+    public void testLintRequireAutomatic(Path base) throws Exception {
+        Path modulePath = base.resolve("module-path");
+
+        Files.createDirectories(modulePath);
+
+        for (char c : new char[] {'A', 'B'}) {
+            Path automaticSrc = base.resolve("automaticSrc" + c);
+            tb.writeJavaFiles(automaticSrc, "package api" + c + "; public class Api {}");
+            Path automaticClasses = base.resolve("automaticClasses" + c);
+            tb.createDirectories(automaticClasses);
+
+            String automaticLog = new JavacTask(tb)
+                                    .outdir(automaticClasses)
+                                    .files(findJavaFiles(automaticSrc))
+                                    .run()
+                                    .writeAll()
+                                    .getOutput(Task.OutputKind.DIRECT);
+
+            if (!automaticLog.isEmpty())
+                throw new Exception("expected output not found: " + automaticLog);
+
+            Path automaticJar = modulePath.resolve("automatic" + c + "-1.0.jar");
+
+            new JarTask(tb, automaticJar)
+              .baseDir(automaticClasses)
+              .files("api" + c + "/Api.class")
+              .run();
+        }
+
+        Path src = base.resolve("src");
+
+        tb.writeJavaFiles(src,
+                          "module m1x {\n" +
+                          "    requires transitive automaticA;\n" +
+                          "    requires automaticB;\n" +
+                          "}");
+
+        Path classes = base.resolve("classes");
+
+        Files.createDirectories(classes);
+
+        List<String> expected;
+        List<String> log;
+
+        log = new JavacTask(tb)
+            .options("--source-path", src.toString(),
+                     "--module-path", modulePath.toString(),
+                     "-XDrawDiagnostics",
+                     "-Werror")
+            .outdir(classes)
+            .files(findJavaFiles(src))
+            .run(Task.Expect.FAIL)
+            .writeAll()
+            .getOutputLines(Task.OutputKind.DIRECT);
+
+        expected = Arrays.asList("module-info.java:2:25: compiler.warn.requires.transitive.automatic",
+                                 "- compiler.err.warnings.and.werror",
+                                 "1 error",
+                                 "1 warning");
+
+        if (!expected.equals(log)) {
+            throw new Exception("expected output not found: " + log);
+        }
+
+        log = new JavacTask(tb)
+            .options("--source-path", src.toString(),
+                     "--module-path", modulePath.toString(),
+                     "-Xlint:requires-automatic",
+                     "-XDrawDiagnostics",
+                     "-Werror")
+            .outdir(classes)
+            .files(findJavaFiles(src))
+            .run(Task.Expect.FAIL)
+            .writeAll()
+            .getOutputLines(Task.OutputKind.DIRECT);
+
+        expected = Arrays.asList("module-info.java:2:25: compiler.warn.requires.transitive.automatic",
+                                 "module-info.java:3:14: compiler.warn.requires.automatic",
+                                 "- compiler.err.warnings.and.werror",
+                                 "1 error",
+                                 "2 warnings");
+
+        if (!expected.equals(log)) {
+            throw new Exception("expected output not found: " + log);
+        }
+
+        log = new JavacTask(tb)
+            .options("--source-path", src.toString(),
+                     "--module-path", modulePath.toString(),
+                     "-Xlint:-requires-transitive-automatic,requires-automatic",
+                     "-XDrawDiagnostics",
+                     "-Werror")
+            .outdir(classes)
+            .files(findJavaFiles(src))
+            .run(Task.Expect.FAIL)
+            .writeAll()
+            .getOutputLines(Task.OutputKind.DIRECT);
+
+        expected = Arrays.asList("module-info.java:2:25: compiler.warn.requires.automatic",
+                                 "module-info.java:3:14: compiler.warn.requires.automatic",
+                                 "- compiler.err.warnings.and.werror",
+                                 "1 error",
+                                 "2 warnings");
+
+        if (!expected.equals(log)) {
+            throw new Exception("expected output not found: " + log);
+        }
+
+        new JavacTask(tb)
+            .options("--source-path", src.toString(),
+                     "--module-path", modulePath.toString(),
+                     "-Xlint:-requires-transitive-automatic",
+                     "-XDrawDiagnostics",
+                     "-Werror")
+            .outdir(classes)
+            .files(findJavaFiles(src))
+            .run(Task.Expect.SUCCESS)
+            .writeAll()
+            .getOutputLines(Task.OutputKind.DIRECT);
+
+        tb.writeJavaFiles(src,
+                          "@SuppressWarnings(\"requires-transitive-automatic\")\n" +
+                          "module m1x {\n" +
+                          "    requires transitive automaticA;\n" +
+                          "    requires automaticB;\n" +
+                          "}");
+
+        new JavacTask(tb)
+            .options("--source-path", src.toString(),
+                     "--module-path", modulePath.toString(),
+                     "-XDrawDiagnostics",
+                     "-Werror")
+            .outdir(classes)
+            .files(findJavaFiles(src))
+            .run(Task.Expect.SUCCESS)
+            .writeAll()
+            .getOutputLines(Task.OutputKind.DIRECT);
+
+        log = new JavacTask(tb)
+            .options("--source-path", src.toString(),
+                     "--module-path", modulePath.toString(),
+                     "-Xlint:requires-automatic",
+                     "-XDrawDiagnostics",
+                     "-Werror")
+            .outdir(classes)
+            .files(findJavaFiles(src))
+            .run(Task.Expect.FAIL)
+            .writeAll()
+            .getOutputLines(Task.OutputKind.DIRECT);
+
+        expected = Arrays.asList("module-info.java:3:25: compiler.warn.requires.automatic",
+                                 "module-info.java:4:14: compiler.warn.requires.automatic",
+                                 "- compiler.err.warnings.and.werror",
+                                 "1 error",
+                                 "2 warnings");
+
+        if (!expected.equals(log)) {
+            throw new Exception("expected output not found: " + log);
+        }
+
+        tb.writeJavaFiles(src,
+                          "@SuppressWarnings(\"requires-automatic\")\n" +
+                          "module m1x {\n" +
+                          "    requires transitive automaticA;\n" +
+                          "    requires automaticB;\n" +
+                          "}");
+
+        log = new JavacTask(tb)
+            .options("--source-path", src.toString(),
+                     "--module-path", modulePath.toString(),
+                     "-Xlint:requires-automatic",
+                     "-XDrawDiagnostics",
+                     "-Werror")
+            .outdir(classes)
+            .files(findJavaFiles(src))
+            .run(Task.Expect.FAIL)
+            .writeAll()
+            .getOutputLines(Task.OutputKind.DIRECT);
+
+        expected = Arrays.asList("module-info.java:3:25: compiler.warn.requires.transitive.automatic",
+                                 "- compiler.err.warnings.and.werror",
+                                 "1 error",
+                                 "1 warning");
+
+        if (!expected.equals(log)) {
+            throw new Exception("expected output not found: " + log);
+        }
+    }
+
 }