8176045: No compile error when a package is not declared

Fixing handling of otherwise empty files with package clauses and empty files without package clauses.

Reviewed-by: jjg
This commit is contained in:
Jan Lahoda 2017-03-14 08:19:41 +01:00
parent d60b98466f
commit 308a2b9f90
3 changed files with 178 additions and 6 deletions

View File

@ -89,7 +89,6 @@ import com.sun.tools.javac.tree.JCTree.JCExports;
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.ABSTRACT;
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 @@ public class Modules extends JCTree.Visitor {
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 @@ public class Modules extends JCTree.Visitor {
@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 @@ public class Modules extends JCTree.Visitor {
rootModules = null;
warnedMissing.clear();
}
public interface PackageNameFinder {
public Name findPackageNameOf(JavaFileObject jfo);
}
}

View File

@ -412,6 +412,7 @@ public class JavaCompiler {
diags = Factory.instance(context);
finder.sourceCompleter = sourceCompleter;
modules.findPackageInFile = this::findPackageInFile;
moduleFinder.moduleNameFromSourceReader = this::readModuleName;
options = Options.instance(context);
@ -1737,6 +1738,11 @@ public class JavaCompiler {
});
}
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);

View File

@ -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.tree.CompilationUnitTree;
//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 @@ public class EdgeCases extends ModuleTestBase {
}
}
@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);
}
}