blob: 0d83b7b8ce1548e1c05be6af85c81c8baf482e34 [file] [log] [blame]
/*
* Copyright (c) 2018, 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.
*/
/*
* @test
* @bug 8187950
* @summary Handing of BadClassFile exceptions and CompletionFailures
* @library /tools/javac/lib /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* @build JavacTestingAbstractProcessor MissingClassFile
* @run main MissingClassFile
*/
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ErrorType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import toolbox.*;
import toolbox.Task.*;
import com.sun.source.tree.Scope;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;
@SupportedAnnotationTypes("*")
public class MissingClassFile {
ToolBox tb = new ToolBox();
void testPackageContent() throws Exception {
Path base = Paths.get(".");
Path libClasses = compileLib(base,
"package pkg;" +
"public class A {" +
"}",
"package pkg;" +
"public class B {" +
"}");
Files.delete(libClasses.resolve("pkg/B.class"));
try (OutputStream out = Files.newOutputStream(libClasses.resolve("pkg/B.class"))) {
out.write(0);
}
doRunTest(base,
t -> {
PackageElement pe = t.getElements().getPackageElement("pkg");
for (Element el : pe.getEnclosedElements()) {
verifyElement(t, el);
}
},
"",
"pkg.B b;");
}
void testPackageDirectAPI() throws Exception {
Path base = Paths.get(".");
Path libClasses = compileLib(base,
"package pkg;" +
"public class A {" +
"}",
"package pkg;" +
"public class B {" +
"}");
Files.delete(libClasses.resolve("pkg/B.class"));
try (OutputStream out = Files.newOutputStream(libClasses.resolve("pkg/B.class"))) {
out.write(0);
}
Path testSrc = base.resolve("test-src");
tb.createDirectories(testSrc);
tb.writeJavaFiles(testSrc,
"package test;\n" +
"public class Test {\n" +
" void t() {\n" +
" pkg.B b;\n" +
" }\n" +
"}\n");
Path testClasses = base.resolve("test-classes");
tb.createDirectories(testClasses);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
List<String> errors = new ArrayList<>();
try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
com.sun.source.util.JavacTask task = (com.sun.source.util.JavacTask)
compiler.getTask(null,
null,
d -> errors.add(d.getCode()),
Arrays.asList("-XDrawDiagnostics",
"-classpath",
libClasses.toString()),
null,
fm.getJavaFileObjects(tb.findJavaFiles(testSrc)));
task.parse();
PackageElement pe = task.getElements().getPackageElement("pkg");
for (Element el : pe.getEnclosedElements()) {
verifyElement(task, el);
}
task.analyze();
}
List<String> expected = Arrays.asList("compiler.err.cant.access");
if (!expected.equals(errors)) {
throw new IllegalStateException("Expected error not found!");
}
}
void testSuperClass() throws Exception {
doTestCombo("class Test {" +
"}",
"package pkg;" +
"public class A extends # {" +
"}",
"pkg.A x;",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements()
.getTypeElement(t.getElements()
.getModuleElement(""),
"pkg.A");
TypeMirror superclass = a.getSuperclass();
verifyTypeMirror(t, superclass);
assertEquals(TypeKind.ERROR, superclass.getKind());
Element superclassEl = ((DeclaredType) superclass).asElement();
assertEquals(ElementKind.CLASS, superclassEl.getKind());
assertEquals(TypeKind.ERROR, superclassEl.asType().getKind());
TypeMirror originalType = Trees.instance(t).getOriginalType((ErrorType) superclass);
assertEquals(TypeKind.DECLARED, originalType.getKind());
assertEquals(superclassEl, ((DeclaredType) originalType).asElement());
});
doTestCombo("interface Test {" +
"}",
"package pkg;" +
"public class A implements # {" +
"}",
"pkg.A x;",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements().getTypeElement("pkg.A");
TypeMirror superintf = a.getInterfaces().get(0);
verifyTypeMirror(t, superintf);
assertEquals(TypeKind.ERROR, superintf.getKind());
Element superintfEl = ((DeclaredType) superintf).asElement();
//superintfEl.getKind() may be either CLASS or INTERFACE, depending on which class is missing
assertEquals(TypeKind.ERROR, superintfEl.asType().getKind());
TypeMirror originalType = Trees.instance(t).getOriginalType((ErrorType) superintf);
assertEquals(TypeKind.DECLARED, originalType.getKind());
assertEquals(superintfEl, ((DeclaredType) originalType).asElement());
});
doTestCombo("class Test {" +
"}",
"package pkg;" +
"public class A extends # {" +
"}",
"pkg.A x;",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements()
.getTypeElement(t.getElements()
.getModuleElement(""),
"pkg.A");
DeclaredType superclass = (DeclaredType) a.getSuperclass();
superclass.getTypeArguments();
});
doTestCombo("class Test {" +
"}",
"package pkg;" +
"public class A extends # {" +
"}",
"pkg.A x;",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements()
.getTypeElement(t.getElements()
.getModuleElement(""),
"pkg.A");
DeclaredType superclass = (DeclaredType) a.getSuperclass();
superclass.getEnclosingType();
});
}
void testAnnotation() throws Exception {
doTestCombo("@interface Test {" +
"}",
"package pkg;" +
"@#\n" +
"public class A {" +
"}",
"",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements().getTypeElement("pkg.A");
for (AnnotationMirror am : a.getAnnotationMirrors()) {
verifyTypeMirror(t, am.getAnnotationType());
}
});
doTestCombo("@interface Test {" +
" public Class<?> value();" +
"}",
"package pkg;" +
"@#(Object.class)\n" +
"public class A {" +
"}",
"",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements().getTypeElement("pkg.A");
for (AnnotationMirror am : a.getAnnotationMirrors()) {
verifyTypeMirror(t, am.getAnnotationType());
if (am.getAnnotationType().toString().equals(fqn)) {
verifyTypeMirror(t, (TypeMirror) am.getElementValues().values()
.iterator().next().getValue());
}
}
});
doTestCombo("class Test { }",
"package pkg;" +
"@Ann(#.class)\n" +
"public class A {" +
"}" +
"@interface Ann {" +
" public Class<?> value();" +
"}",
"",
"# a = null; a.toString();",
(fqn, t) -> {
TypeElement a = t.getElements().getTypeElement("pkg.A");
for (AnnotationMirror am : a.getAnnotationMirrors()) {
verifyTypeMirror(t, am.getAnnotationType());
if (am.getAnnotationType().toString().equals(fqn)) {
verifyTypeMirror(t, (TypeMirror) am.getElementValues().values()
.iterator().next().getValue());
}
}
});
}
void testMethod() throws Exception {
doTestCombo("class Test {" +
"}",
"package pkg;" +
"public class A {" +
" public void m1(# t) { }" +
" public # m2() { return null; }" +
"}",
"",
"pkg.A a = null; a.m2().toString();",
(fqn, t) -> {
TypeElement a = t.getElements().getTypeElement("pkg.A");
List<? extends Element> members = a.getEnclosedElements();
if (members.size() != 3)
throw new AssertionError("Unexpected number of members, " +
"received members: " + members);
for (Element e : members) {
verifyElement(t, e);
}
});
}
void testAnnotationProcessing() throws Exception {
boolean[] superClass = new boolean[1];
boolean[] inInit = new boolean[1];
class TestAP extends AbstractProcessor {
@Override
public void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
if (inInit[0])
doCheck();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (!inInit[0])
doCheck();
return false;
}
private void doCheck() {
com.sun.source.util.JavacTask t = com.sun.source.util.JavacTask.instance(processingEnv);
TypeElement a = t.getElements().getTypeElement("pkg.A");
if (superClass[0]) {
verifyTypeMirror(t, a.getSuperclass());
} else {
verifyTypeMirror(t, a.getInterfaces().get(0));
}
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return Set.of("*");
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
}
for (boolean supClass : new boolean[] {false, true}) {
for (boolean init : new boolean[] {false, true}) {
String decl = supClass ? "class Test { }" : "interface Test { }";
String snip = supClass ? "extends #" : "implements #";
superClass[0] = supClass;
inInit[0] = init;
doTestComboCallBack(decl,
"package pkg;" +
"public class A " + snip + " {" +
"}",
"",
"# a = null; a.toString();",
(fqn, t) -> t.setProcessors(List.of(new TestAP())));
}
}
}
void testGetTypeElement() throws Exception {
doTestCombo("class Test { }",
"package pkg;" +
"public class A extends # {" +
"}",
"",
"pkg.A a = null; a.toString();", //should be generalized/in variant?
(fqn, t) -> {
TypeElement a = t.getElements().getTypeElement(fqn);
if (a != null) {
throw new IllegalStateException();
}
});
}
void testScope() throws Exception {
class Variant {
private final String code;
private final String fqn;
public Variant(String code, String fqn) {
this.code = code;
this.fqn = fqn;
}
}
Path base = Paths.get(".");
Path libClasses = compileLib(base,
"package pkg;" +
"public class A {" +
" public static class I {}" +
"}",
"package pkg;" +
"public class B {" +
"}");
try (OutputStream out = Files.newOutputStream(libClasses.resolve("pkg/B.class"))) {
out.write(0);
}
try (OutputStream out = Files.newOutputStream(libClasses.resolve("pkg/A$I.class"))) {
out.write(0);
}
Path testSrc = base.resolve("test-src");
tb.createDirectories(testSrc);
Path testClasses = base.resolve("test-classes");
tb.createDirectories(testClasses);
Variant[] variants = new Variant[] {
//JDK-8198378:
// new Variant("package test;\n" +
// "import pkg.B;\n" +
// "public class Test {}\n",
// "test.Test"),
new Variant("package test;\n" +
"import pkg.*;\n" +
"public class Test {}\n",
"test.Test"),
new Variant("package test;\n" +
"import pkg.A.*;\n" +
"public class Test extends I {}\n",
"test.Test"),
new Variant("package test;\n" +
"import static pkg.A.*;\n" +
"public class Test extends I {}\n",
"test.Test"),
new Variant("package pkg;\n" +
"public class Test {}\n",
"pkg.Test")
};
for (Variant variant : variants) {
System.err.println("variant: " + variant.code);
tb.writeJavaFiles(testSrc, variant.code);
tb.cleanDirectory(testClasses);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
List<String> errors = new ArrayList<>();
try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) {
com.sun.source.util.JavacTask task = (com.sun.source.util.JavacTask)
compiler.getTask(null,
null,
d -> errors.add(d.getCode()),
Arrays.asList("-XDrawDiagnostics",
"-classpath",
libClasses.toString()),
null,
fm.getJavaFileObjects(tb.findJavaFiles(testSrc)));
task.analyze();
TypeElement a = task.getElements()
.getTypeElement(task.getElements()
.getModuleElement(""),
variant.fqn);
Trees trees = Trees.instance(task);
TreePath tpA = trees.getPath(a);
Scope scope = trees.getScope(tpA);
while (scope != null) {
for (Element el : scope.getLocalElements()) {
verifyElement(task, el);
}
scope = scope.getEnclosingScope();
}
}
}
}
private Path compileLib(Path base, String... sources) throws Exception {
Path libSrc = base.resolve("lib-src");
tb.createDirectories(libSrc);
tb.writeJavaFiles(libSrc, sources);
Path libClasses = base.resolve("lib-classes");
tb.createDirectories(libClasses);
new JavacTask(tb).outdir(libClasses.toString())
.sourcepath(libSrc.toString())
.files(tb.findJavaFiles(libSrc))
.run()
.writeAll();
return libClasses;
}
private void doTestCombo(String decl,
String use,
String snippetInClass,
String snippetInMethod,
BiConsumer<String, com.sun.source.util.JavacTask> test) throws Exception {
doTestComboCallBack(decl,
use,
snippetInClass,
snippetInMethod,
(fqn, t) -> {
t.addTaskListener(new TaskListener() {
@Override
public void finished(TaskEvent e) {
if (e.getKind() == TaskEvent.Kind.ENTER) {
test.accept(fqn, t);
}
}
});
});
}
private void doTestComboCallBack(String decl,
String use,
String snippetInClass,
String snippetInMethod,
BiConsumer<String, com.sun.source.util.JavacTask> callback) throws Exception {
List<TestVariant> variants = List.of(
new TestVariant("package pkg; public #", "pkg.Test", "pkg/Test.class"),
new TestVariant("package pkg; public class O { public static # }", "pkg.O.Test", "pkg/O$Test.class"),
new TestVariant("package pkg; public class O { public static # }", "pkg.O.Test", "pkg/O.class"),
new TestVariant("package pkg; public class O { public static class N { public static # } }", "pkg.O.N.Test", "pkg/O$N$Test.class"),
new TestVariant("package pkg; public class O { public static class N { public static # } }", "pkg.O.N.Test", "pkg/O$N.class"),
new TestVariant("package pkg; public class O { public static class N { public static # } }", "pkg.O.N.Test", "pkg/O.class")
);
Path base = Paths.get(".");
for (TestVariant v : variants) {
System.err.println("-----------------------------------------------------------------------");
System.err.println("variant: " + v.declarationStub + ", " + v.fqn + ", " + v.path);
Path libClasses = compileLib(base,
use.replace("#", v.fqn),
v.declarationStub.replace("#", decl));
Files.delete(libClasses.resolve(v.path));
doRunTestFullCallback(base,
t -> callback.accept(v.fqn, t),
snippetInClass.replace("#", v.fqn),
snippetInMethod.replace("#", v.fqn));
}
}
private void doRunTest(Path base,
Consumer<com.sun.source.util.JavacTask> test,
String snippetInClass,
String snippetInMethod) throws Exception {
doRunTestFullCallback(base, t -> {
t.addTaskListener(new TaskListener() {
@Override
public void finished(TaskEvent e) {
if (e.getKind() == TaskEvent.Kind.ENTER) {
test.accept(t);
}
}
});
}, snippetInClass, snippetInMethod);
}
private void doRunTestFullCallback(Path base,
Consumer<com.sun.source.util.JavacTask> callback,
String snippetInClass,
String snippetInMethod) throws Exception {
Path libClasses = base.resolve("lib-classes");
Path testSrc = base.resolve("test-src");
tb.createDirectories(testSrc);
tb.writeJavaFiles(testSrc,
"package test;\n" +
"public class Test {\n" +
snippetInClass + "\n" +
" void t() {\n" +
snippetInMethod + "\n" +
" }\n" +
"}\n");
System.err.println("content: " + "package test;\n" +
"public class Test {\n" +
snippetInClass + "\n" +
" void t() {\n" +
snippetInMethod + "\n" +
" }\n" +
"}\n");
Path testClasses = base.resolve("test-classes");
tb.createDirectories(testClasses);
var expectedErrors = new JavacTask(tb).outdir(testClasses.toString())
.options("-XDrawDiagnostics",
"-classpath",
libClasses.toString())
.sourcepath(testSrc.toString())
.files(tb.findJavaFiles(testSrc))
.run(Expect.FAIL)
.writeAll()
.getOutputLines(OutputKind.DIRECT,
OutputKind.STDERR,
OutputKind.STDOUT);
var errors = new JavacTask(tb).outdir(testClasses.toString())
.options("-XDrawDiagnostics",
"-classpath",
libClasses.toString())
.sourcepath(testSrc.toString())
.files(tb.findJavaFiles(testSrc))
.callback(callback)
.run(Expect.FAIL)
.writeAll()
.getOutputLines(OutputKind.DIRECT,
OutputKind.STDERR,
OutputKind.STDOUT);
if (!expectedErrors.equals(errors)) {
throw new IllegalStateException("Expected error not found!");
}
}
private void verifyTypeMirror(com.sun.source.util.JavacTask t, TypeMirror type) {
Element el = t.getTypes().asElement(type);
if (el != null) {
verifyElement(t, el);
}
}
private void verifyElement(com.sun.source.util.JavacTask t, Element el) {
el.getKind(); //forces completion
}
private static void assertEquals(Object expected, Object actual) {
if (!Objects.equals(expected, actual)) {
throw new AssertionError("Unexpected value, expected: " + expected + ", actual: " + actual);
}
}
public static void main(String... args) throws Exception {
MissingClassFile t = new MissingClassFile();
t.testPackageContent();
t.testPackageDirectAPI();
t.testSuperClass();
t.testAnnotation();
t.testAnnotationProcessing();
t.testGetTypeElement();
t.testScope();
}
static class TestVariant {
public final String declarationStub;
public final String fqn;
public final String path;
public TestVariant(String declarationStub, String fqn, String path) {
this.declarationStub = declarationStub;
this.fqn = fqn;
this.path = path;
}
}
}