8176045: No compile error when a package is not declared
Summary: Fixing handling of otherwise empty files with package clauses and empty files without package clauses.
Reviewed-by: jjg
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java Tue Mar 14 07:11:45 2017 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java Tue Mar 14 08:19:41 2017 +0100
@@ -89,7 +89,6 @@
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCModuleDecl;
import com.sun.tools.javac.tree.JCTree.JCOpens;
-import com.sun.tools.javac.tree.JCTree.JCPackageDecl;
import com.sun.tools.javac.tree.JCTree.JCProvides;
import com.sun.tools.javac.tree.JCTree.JCRequires;
import com.sun.tools.javac.tree.JCTree.JCUses;
@@ -112,6 +111,7 @@
import static com.sun.tools.javac.code.Flags.ENUM;
import static com.sun.tools.javac.code.Flags.PUBLIC;
import static com.sun.tools.javac.code.Flags.UNATTRIBUTED;
+import com.sun.tools.javac.code.Kinds;
import static com.sun.tools.javac.code.Kinds.Kind.ERR;
import static com.sun.tools.javac.code.Kinds.Kind.MDL;
import static com.sun.tools.javac.code.Kinds.Kind.MTH;
@@ -167,6 +167,8 @@
private Set<ModuleSymbol> rootModules = null;
private final Set<ModuleSymbol> warnedMissing = new HashSet<>();
+ public PackageNameFinder findPackageInFile;
+
public static Modules instance(Context context) {
Modules instance = context.get(Modules.class);
if (instance == null)
@@ -956,7 +958,30 @@
@Override
public void visitExports(JCExports tree) {
- if (tree.directive.packge.members().isEmpty()) {
+ Iterable<Symbol> packageContent = tree.directive.packge.members().getSymbols();
+ List<JavaFileObject> filesToCheck = List.nil();
+ boolean packageNotEmpty = false;
+ for (Symbol sym : packageContent) {
+ if (sym.kind != Kinds.Kind.TYP)
+ continue;
+ ClassSymbol csym = (ClassSymbol) sym;
+ if (sym.completer.isTerminal() ||
+ csym.classfile.getKind() == Kind.CLASS) {
+ packageNotEmpty = true;
+ filesToCheck = List.nil();
+ break;
+ }
+ if (csym.classfile.getKind() == Kind.SOURCE) {
+ filesToCheck = filesToCheck.prepend(csym.classfile);
+ }
+ }
+ for (JavaFileObject jfo : filesToCheck) {
+ if (findPackageInFile.findPackageNameOf(jfo) == tree.directive.packge.fullname) {
+ packageNotEmpty = true;
+ break;
+ }
+ }
+ if (!packageNotEmpty) {
log.error(tree.qualid.pos(), Errors.PackageEmptyOrNotFound(tree.directive.packge));
}
msym.directives = msym.directives.prepend(tree.directive);
@@ -1676,4 +1701,8 @@
rootModules = null;
warnedMissing.clear();
}
+
+ public interface PackageNameFinder {
+ public Name findPackageNameOf(JavaFileObject jfo);
+ }
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Tue Mar 14 07:11:45 2017 +0100
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Tue Mar 14 08:19:41 2017 +0100
@@ -412,6 +412,7 @@
diags = Factory.instance(context);
finder.sourceCompleter = sourceCompleter;
+ modules.findPackageInFile = this::findPackageInFile;
moduleFinder.moduleNameFromSourceReader = this::readModuleName;
options = Options.instance(context);
@@ -1737,6 +1738,11 @@
});
}
+ private Name findPackageInFile(JavaFileObject fo) {
+ return parseAndGetName(fo, t -> t.getPackage() != null ?
+ TreeInfo.fullName(t.getPackage().getPackageName()) : null);
+ }
+
private Name parseAndGetName(JavaFileObject fo,
Function<JCTree.JCCompilationUnit, Name> tree2Name) {
DiagnosticHandler dh = new DiscardDiagnosticHandler(log);
--- a/langtools/test/tools/javac/modules/EdgeCases.java Tue Mar 14 07:11:45 2017 +0100
+++ b/langtools/test/tools/javac/modules/EdgeCases.java Tue Mar 14 08:19:41 2017 +0100
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8154283 8167320 8171098 8172809 8173068 8173117
+ * @bug 8154283 8167320 8171098 8172809 8173068 8173117 8176045
* @summary tests for multi-module mode compilation
* @library /tools/lib
* @modules
@@ -36,6 +36,7 @@
* @run main EdgeCases
*/
+import java.io.BufferedWriter;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -67,10 +68,7 @@
//import com.sun.source.util.JavacTask; // conflicts with toolbox.JavacTask
import com.sun.tools.javac.api.JavacTaskImpl;
import com.sun.tools.javac.code.Symbol.ModuleSymbol;
-import com.sun.tools.javac.code.Symbol.PackageSymbol;
import com.sun.tools.javac.code.Symtab;
-import com.sun.tools.javac.processing.JavacProcessingEnvironment;
-import com.sun.tools.javac.util.Context;
import toolbox.JarTask;
import toolbox.JavacTask;
@@ -821,4 +819,143 @@
}
}
+
+ @Test
+ public void testEmptyInExportedPackage(Path base) throws Exception {
+ Path src = base.resolve("src");
+ Path m = src.resolve("m");
+ tb.writeJavaFiles(m,
+ "module m { exports api; }");
+ Path apiFile = m.resolve("api").resolve("Api.java");
+ Files.createDirectories(apiFile.getParent());
+ try (BufferedWriter w = Files.newBufferedWriter(apiFile)) {
+ w.write("//no package decl");
+ }
+ Path classes = base.resolve("classes");
+ tb.createDirectories(classes);
+
+ List<String> log;
+ List<String> expected =
+ Arrays.asList("module-info.java:1:20: compiler.err.package.empty.or.not.found: api",
+ "1 error");
+
+ System.err.println("file explicitly specified:");
+
+ log = new JavacTask(tb)
+ .options("-XDrawDiagnostics",
+ "--module-source-path", src.toString())
+ .outdir(classes)
+ .files(findJavaFiles(src))
+ .run(Task.Expect.FAIL)
+ .writeAll()
+ .getOutputLines(Task.OutputKind.DIRECT);
+
+ if (!expected.equals(log))
+ throw new Exception("expected output not found: " + log);
+
+ System.err.println("file not specified:");
+
+ tb.cleanDirectory(classes);
+
+ log = new JavacTask(tb)
+ .options("-XDrawDiagnostics",
+ "--module-source-path", src.toString())
+ .outdir(classes)
+ .files(findJavaFiles(m.resolve("module-info.java")))
+ .run(Task.Expect.FAIL)
+ .writeAll()
+ .getOutputLines(Task.OutputKind.DIRECT);
+
+ if (!expected.equals(log))
+ throw new Exception("expected output not found: " + log);
+ }
+
+ @Test
+ public void testJustPackageInExportedPackage(Path base) throws Exception {
+ Path src = base.resolve("src");
+ Path m = src.resolve("m");
+ tb.writeJavaFiles(m,
+ "module m { exports api; }");
+ Path apiFile = m.resolve("api").resolve("Api.java");
+ Files.createDirectories(apiFile.getParent());
+ try (BufferedWriter w = Files.newBufferedWriter(apiFile)) {
+ w.write("package api;");
+ }
+ Path classes = base.resolve("classes");
+ tb.createDirectories(classes);
+
+ System.err.println("file explicitly specified:");
+
+ new JavacTask(tb)
+ .options("-XDrawDiagnostics",
+ "--module-source-path", src.toString())
+ .outdir(classes)
+ .files(findJavaFiles(src))
+ .run()
+ .writeAll();
+
+ System.err.println("file not specified:");
+
+ tb.cleanDirectory(classes);
+
+ new JavacTask(tb)
+ .options("-XDrawDiagnostics",
+ "--module-source-path", src.toString())
+ .outdir(classes)
+ .files(findJavaFiles(m.resolve("module-info.java")))
+ .run()
+ .writeAll();
+ }
+
+ @Test
+ public void testWrongPackageInExportedPackage(Path base) throws Exception {
+ Path src = base.resolve("src");
+ Path m = src.resolve("m");
+ tb.writeJavaFiles(m,
+ "module m { exports api; }");
+ Path apiFile = m.resolve("api").resolve("Api.java");
+ Files.createDirectories(apiFile.getParent());
+ try (BufferedWriter w = Files.newBufferedWriter(apiFile)) {
+ w.write("package impl; public class Api { }");
+ }
+ Path classes = base.resolve("classes");
+ tb.createDirectories(classes);
+
+ List<String> log;
+
+ List<String> expected =
+ Arrays.asList("module-info.java:1:20: compiler.err.package.empty.or.not.found: api",
+ "1 error");
+
+ System.err.println("file explicitly specified:");
+
+ log = new JavacTask(tb)
+ .options("-XDrawDiagnostics",
+ "--module-source-path", src.toString())
+ .outdir(classes)
+ .files(findJavaFiles(src))
+ .run(Task.Expect.FAIL)
+ .writeAll()
+ .getOutputLines(Task.OutputKind.DIRECT);
+
+ if (!expected.equals(log))
+ throw new Exception("expected output not found: " + log);
+
+ System.err.println("file not specified:");
+
+ tb.cleanDirectory(classes);
+
+ log = new JavacTask(tb)
+ .options("-XDrawDiagnostics",
+ "--module-source-path", src.toString())
+ .outdir(classes)
+ .files(findJavaFiles(m.resolve("module-info.java")))
+ .run(Task.Expect.FAIL)
+ .writeAll()
+ .getOutputLines(Task.OutputKind.DIRECT);
+
+ if (!expected.equals(log))
+ throw new Exception("expected output not found: " + log);
+ }
+
}