6550655: com.sun.tools.javac.code.Symbol$CompletionFailure
Summary: Accessing a non-existing enum constant from an annotation whose class is available results in an internal error
Reviewed-by: jjg
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java Fri Apr 29 16:05:56 2011 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java Fri Apr 29 16:06:28 2011 +0100
@@ -168,11 +168,11 @@
}
JCIdent left = (JCIdent)assign.lhs;
Symbol method = rs.resolveQualifiedMethod(left.pos(),
- env,
- a.type,
- left.name,
- List.<Type>nil(),
- null);
+ env,
+ a.type,
+ left.name,
+ List.<Type>nil(),
+ null);
left.sym = method;
left.type = method.type;
if (method.owner != a.type.tsym)
@@ -190,6 +190,15 @@
Attribute enterAttributeValue(Type expected,
JCExpression tree,
Env<AttrContext> env) {
+ //first, try completing the attribution value sym - if a completion
+ //error is thrown, we should recover gracefully, and display an
+ //ordinary resolution diagnostic.
+ try {
+ expected.tsym.complete();
+ } catch(CompletionFailure e) {
+ log.error(tree.pos(), "cant.resolve", Kinds.kindName(e.sym), e.sym);
+ return new Attribute.Error(expected);
+ }
if (expected.isPrimitive() || types.isSameType(expected, syms.stringType)) {
Type result = attr.attribExpr(tree, env, expected);
if (result.isErroneous())
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Fri Apr 29 16:05:56 2011 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Fri Apr 29 16:06:28 2011 +0100
@@ -1609,18 +1609,31 @@
// type.tsym.flatName() should == proxy.enumFlatName
TypeSymbol enumTypeSym = proxy.enumType.tsym;
VarSymbol enumerator = null;
- for (Scope.Entry e = enumTypeSym.members().lookup(proxy.enumerator);
- e.scope != null;
- e = e.next()) {
- if (e.sym.kind == VAR) {
- enumerator = (VarSymbol)e.sym;
- break;
+ CompletionFailure failure = null;
+ try {
+ for (Scope.Entry e = enumTypeSym.members().lookup(proxy.enumerator);
+ e.scope != null;
+ e = e.next()) {
+ if (e.sym.kind == VAR) {
+ enumerator = (VarSymbol)e.sym;
+ break;
+ }
}
}
+ catch (CompletionFailure ex) {
+ failure = ex;
+ }
if (enumerator == null) {
- log.error("unknown.enum.constant",
- currentClassFile, enumTypeSym, proxy.enumerator);
- result = new Attribute.Error(enumTypeSym.type);
+ if (failure != null) {
+ log.warning("unknown.enum.constant.reason",
+ currentClassFile, enumTypeSym, proxy.enumerator,
+ failure.getDiagnostic());
+ } else {
+ log.warning("unknown.enum.constant",
+ currentClassFile, enumTypeSym, proxy.enumerator);
+ }
+ result = new Attribute.Enum(enumTypeSym.type,
+ new VarSymbol(0, proxy.enumerator, syms.botType, enumTypeSym));
} else {
result = new Attribute.Enum(enumTypeSym.type, enumerator);
}
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Fri Apr 29 16:05:56 2011 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties Fri Apr 29 16:06:28 2011 +0100
@@ -762,9 +762,6 @@
compiler.err.unclosed.str.lit=\
unclosed string literal
-compiler.err.unknown.enum.constant=\
- in class file {0}: unknown enum constant {1}.{2}
-
# 0: name
compiler.err.unsupported.encoding=\
unsupported encoding: {0}
@@ -1307,6 +1304,15 @@
compiler.warn.annotation.method.not.found.reason=\
Cannot find annotation method ''{1}()'' in type ''{0}'': {2}
+# 0: symbol, 1: name
+compiler.warn.unknown.enum.constant=\
+ unknown enum constant {1}.{2}
+
+# 0: symbol, 1: name, 2: message segment
+compiler.warn.unknown.enum.constant.reason=\
+ unknown enum constant {1}.{2}\n\
+ reason: {3}
+
# 0: type, 1: type
compiler.warn.raw.class.use=\
found raw type: {0}\n\
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/annotations/6550655/T6550655.java Fri Apr 29 16:06:28 2011 +0100
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2011, 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 6550655
+ * @summary javac crashes when compiling against an annotated class
+ */
+
+import java.io.File;
+import java.net.URI;
+import java.util.Arrays;
+
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaCompiler.CompilationTask;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.ToolProvider;
+
+public class T6550655 {
+
+ JavaCompiler javacTool;
+ File testDir;
+ TestKind testKind;
+ EnumActionKind actionKind;
+
+ String testSource = "enum E { NORTH, SOUTH, WEST, EAST; }\n" +
+ "@I(val = E.NORTH)class A {}\n" +
+ "@interface I { E val(); }";
+
+ T6550655(JavaCompiler javacTool, File testDir, TestKind testKind, EnumActionKind actionKind) {
+ this.javacTool = javacTool;
+ this.testDir = testDir;
+ this.testKind = testKind;
+ this.actionKind = actionKind;
+ }
+
+ void test() {
+ testDir.mkdirs();
+ compile(null, new JavaSource("Test.java", testSource));
+ actionKind.doAction(this);
+ compile(new DiagnosticChecker(), testKind.source);
+ }
+
+ void compile(DiagnosticChecker dc, JavaSource... sources) {
+ try {
+ CompilationTask ct = javacTool.getTask(null, null, dc,
+ Arrays.asList("-d", testDir.getAbsolutePath(), "-cp", testDir.getAbsolutePath()),
+ null, Arrays.asList(sources));
+ ct.call();
+ }
+ catch (Exception e) {
+ error("Internal compilation error");
+ }
+ }
+
+ void replaceEnum(String newSource) {
+ compile(null, new JavaSource("Replace.java", newSource));
+ };
+
+ void removeEnum() {
+ File enumClass = new File(testDir, "E.class");
+ if (!enumClass.exists()) {
+ error("Expected file E.class does not exists in folder " + testDir);
+ }
+ enumClass.delete();
+ };
+
+ void error(String msg) {
+ System.err.println(msg);
+ nerrors++;
+ }
+
+ class DiagnosticChecker implements DiagnosticListener<JavaFileObject> {
+
+ String[][] expectedKeys = new String[][] {
+ // DIRECT, INDIRECT
+ {/*REPLACE1*/ "compiler.err.cant.resolve.location" , "compiler.warn.unknown.enum.constant" },
+ {/*REPLACE2*/ "compiler.err.cant.resolve.location.args", "compiler.warn.annotation.method.not.found" },
+ {/*REMOVE*/ "compiler.err.cant.resolve" , "compiler.warn.unknown.enum.constant.reason" } };
+
+ String keyToIgnore = "compiler.err.attribute.value.must.be.constant";
+
+ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+ String expectedCode = expectedKeys[actionKind.ordinal()][testKind.ordinal()];
+ if (!diagnostic.getCode().equals(keyToIgnore) &&
+ !diagnostic.getCode().equals(expectedCode)) {
+ error("Unexpected diagnostic" +
+ "\nfound " + diagnostic.getCode() +
+ "\nexpected " + expectedCode +
+ "\ntestKind " + testKind +
+ "\nactionKind " + actionKind);
+ }
+ }
+ }
+
+ //global declarations
+
+ enum EnumActionKind {
+ REPLACE1("enum E { SOUTH, WEST, EAST; }") {
+ @Override
+ void doAction(T6550655 test) {
+ test.replaceEnum(optionalSource);
+ }
+ },
+ REPLACE2("@interface I { E valNew() default E.EAST; }") {
+ @Override
+ void doAction(T6550655 test) {
+ test.replaceEnum(optionalSource);
+ }
+ },
+ REMOVE(null) {
+ @Override
+ void doAction(T6550655 test) { test.removeEnum(); }
+ };
+
+ String optionalSource;
+
+ private EnumActionKind(String optionalSource) {
+ this.optionalSource = optionalSource;
+ }
+
+ abstract void doAction(T6550655 test);
+ }
+
+ enum TestKind {
+ DIRECT("@I(val = E.NORTH)class C1 {}"),
+ INDIRECT("class C2 { A a; }");
+
+ JavaSource source;
+
+ private TestKind(final String code) {
+ this.source = new JavaSource("Test.java", code);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ String SCRATCH_DIR = System.getProperty("user.dir");
+ JavaCompiler javacTool = ToolProvider.getSystemJavaCompiler();
+ int n = 0;
+ for (TestKind testKind : TestKind.values()) {
+ for (EnumActionKind actionKind : EnumActionKind.values()) {
+ File testDir = new File(SCRATCH_DIR, "test"+n);
+ new T6550655(javacTool, testDir, testKind, actionKind).test();
+ n++;
+ }
+ }
+ if (nerrors > 0) {
+ throw new AssertionError("Some errors have been detected");
+ }
+ }
+
+ static class JavaSource extends SimpleJavaFileObject {
+
+ String source;
+
+ public JavaSource(String filename, String source) {
+ super(URI.create("myfo:/" + filename), JavaFileObject.Kind.SOURCE);
+ this.source = source;
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return source;
+ }
+ }
+
+ static int nerrors = 0;
+}
--- a/langtools/test/tools/javac/diags/examples.not-yet.txt Fri Apr 29 16:05:56 2011 +0100
+++ b/langtools/test/tools/javac/diags/examples.not-yet.txt Fri Apr 29 16:06:28 2011 +0100
@@ -41,7 +41,6 @@
compiler.err.type.var.more.than.once.in.result # UNUSED
compiler.err.undetermined.type
compiler.err.unexpected.type
-compiler.err.unknown.enum.constant # in bad class file
compiler.err.unsupported.cross.fp.lit # Scanner: host system dependent
compiler.err.wrong.target.for.polymorphic.signature.definition # Transitional 292
compiler.misc.assignment.from.super-bound
@@ -112,3 +111,5 @@
compiler.warn.unchecked.assign # DEAD, replaced by compiler.misc.unchecked.assign
compiler.warn.unchecked.cast.to.type # DEAD, replaced by compiler.misc.unchecked.cast.to.type
compiler.warn.unexpected.archive.file # Paths: zip file with unknown extn
+compiler.warn.unknown.enum.constant # in bad class file
+compiler.warn.unknown.enum.constant.reason # in bad class file