6550655: com.sun.tools.javac.code.Symbol$CompletionFailure
authormcimadamore
Fri, 29 Apr 2011 16:06:28 +0100
changeset 9603 fa337b87574b
parent 9602 5efe2fa1581a
child 9604 d7152128d22f
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
langtools/src/share/classes/com/sun/tools/javac/comp/Annotate.java
langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java
langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties
langtools/test/tools/javac/annotations/6550655/T6550655.java
langtools/test/tools/javac/diags/examples.not-yet.txt
--- 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