8135307: CompletionFailure thrown when calling FieldDoc.type, if the field's type is missing
authorjlahoda
Wed, 23 Sep 2015 08:29:33 +0200
changeset 32800 8457813125e3
parent 32799 ee577901f4bb
child 32801 e69dd1decf7b
8135307: CompletionFailure thrown when calling FieldDoc.type, if the field's type is missing Summary: Handling CompletionFailures inside the Javadoc API implementation. Reviewed-by: mcimadamore, ksrini, jjg
langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/ClassDocImpl.java
langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/MethodDocImpl.java
langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/TypeMaker.java
langtools/test/tools/javadoc/CompletionError.java
--- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/ClassDocImpl.java	Mon Sep 21 11:19:10 2015 +0200
+++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/ClassDocImpl.java	Wed Sep 23 08:29:33 2015 +0200
@@ -126,19 +126,14 @@
      * Returns the flags of a ClassSymbol in terms of javac's flags
      */
     static long getFlags(ClassSymbol clazz) {
-        while (true) {
-            try {
-                return clazz.flags();
-            } catch (CompletionFailure ex) {
-                /* Quietly ignore completion failures.
-                 * Note that a CompletionFailure can only
-                 * occur as a result of calling complete(),
-                 * which will always remove the current
-                 * completer, leaving it to be null or
-                 * follow-up completer. Thus the loop
-                 * is guaranteed to eventually terminate.
-                 */
-            }
+        try {
+            return clazz.flags();
+        } catch (CompletionFailure ex) {
+            /* Quietly ignore completion failures and try again - the type
+             * for which the CompletionFailure was thrown shouldn't be completed
+             * again by the completer that threw the CompletionFailure.
+             */
+            return getFlags(clazz);
         }
     }
 
--- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/MethodDocImpl.java	Mon Sep 21 11:19:10 2015 +0200
+++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/MethodDocImpl.java	Wed Sep 23 08:29:33 2015 +0200
@@ -128,7 +128,7 @@
              t.hasTag(CLASS);
              t = env.types.supertype(t)) {
             ClassSymbol c = (ClassSymbol)t.tsym;
-            for (Symbol sym2 : c.members().getSymbolsByName(sym.name)) {
+            for (Symbol sym2 : membersOf(c).getSymbolsByName(sym.name)) {
                 if (sym.overrides(sym2, origin, env.types, true)) {
                     return TypeMaker.getType(env, t);
                 }
@@ -160,7 +160,7 @@
              t.hasTag(CLASS);
              t = env.types.supertype(t)) {
             ClassSymbol c = (ClassSymbol)t.tsym;
-            for (Symbol sym2 : c.members().getSymbolsByName(sym.name)) {
+            for (Symbol sym2 : membersOf(c).getSymbolsByName(sym.name)) {
                 if (sym.overrides(sym2, origin, env.types, true)) {
                     return env.getMethodDoc((MethodSymbol)sym2);
                 }
@@ -169,6 +169,19 @@
         return null;
     }
 
+    /**Retrieve members of c, ignoring any CompletionFailures that occur. */
+    private Scope membersOf(ClassSymbol c) {
+        try {
+            return c.members();
+        } catch (CompletionFailure cf) {
+            /* Quietly ignore completion failures and try again - the type
+             * for which the CompletionFailure was thrown shouldn't be completed
+             * again by the completer that threw the CompletionFailure.
+             */
+            return membersOf(c);
+        }
+    }
+
     /**
      * Tests whether this method overrides another.
      * The overridden method may be one declared in a superclass or
--- a/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/TypeMaker.java	Mon Sep 21 11:19:10 2015 +0200
+++ b/langtools/src/jdk.javadoc/share/classes/com/sun/tools/javadoc/TypeMaker.java	Wed Sep 23 08:29:33 2015 +0200
@@ -28,6 +28,7 @@
 import com.sun.javadoc.*;
 import com.sun.tools.javac.code.Symbol;
 import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.CompletionFailure;
 import com.sun.tools.javac.code.Type;
 import com.sun.tools.javac.code.Type.ArrayType;
 import com.sun.tools.javac.code.Type.ClassType;
@@ -56,8 +57,21 @@
         return getType(env, t, errorToClassDoc, true);
     }
 
+    public static com.sun.javadoc.Type getType(DocEnv env, Type t,
+            boolean errToClassDoc, boolean considerAnnotations) {
+        try {
+            return getTypeImpl(env, t, errToClassDoc, considerAnnotations);
+        } catch (CompletionFailure cf) {
+            /* Quietly ignore completion failures and try again - the type
+             * for which the CompletionFailure was thrown shouldn't be completed
+             * again by the completer that threw the CompletionFailure.
+             */
+            return getType(env, t, errToClassDoc, considerAnnotations);
+        }
+    }
+
     @SuppressWarnings("fallthrough")
-    public static com.sun.javadoc.Type getType(DocEnv env, Type t,
+    private static com.sun.javadoc.Type getTypeImpl(DocEnv env, Type t,
             boolean errToClassDoc, boolean considerAnnotations) {
         if (env.legacyDoclet) {
             t = env.types.erasure(t);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javadoc/CompletionError.java	Wed Sep 23 08:29:33 2015 +0200
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2015, 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 8135307
+ * @summary Check that CompletionFailures for missing classes are not incorrectly passed to
+ *          the javadoc API clients.
+ * @library /tools/lib
+ * @modules jdk.javadoc
+ * @run main CompletionError
+ */
+
+import java.io.File;
+
+import com.sun.javadoc.*;
+import com.sun.tools.javadoc.Main;
+
+public class CompletionError extends Doclet
+{
+    private static final String template =
+            "public class CompletionErrorAuxiliary #extends CompletionErrorMissing# #implements CompletionErrorIntfMissing# {" +
+            "   #public CompletionErrorMissing tf;#" +
+            "   #public CompletionErrorMissing tm() { return null; }#" +
+            "   #public void tm(CompletionErrorMissing m) {}#" +
+            "   #public void tm() throws CompletionErrorExcMissing {}#" +
+            "   #public <T extends CompletionErrorMissing> void tm() {}#" +
+            "   public String toString() { return null; }" +
+            "}";
+
+    public static void main(String[] args) throws Exception {
+        String[] templateParts = template.split("#");
+        int sources = templateParts.length / 2;
+        for (int source = 0; source < sources; source++) {
+            StringBuilder testSource = new StringBuilder();
+            for (int i = 0; i < templateParts.length; i += 2) {
+                testSource.append(templateParts[i]);
+                if (i == 2 * source) {
+                    testSource.append(templateParts[i + 1]);
+                }
+            }
+            test = 0;
+            testsDone = false;
+            while (!testsDone) {
+                ToolBox tb = new ToolBox();
+                tb.new JavacTask()
+                  .sources(testSource.toString(),
+                           "public class CompletionErrorMissing {}",
+                           "public interface CompletionErrorIntfMissing {}",
+                           "public class CompletionErrorExcMissing extends Exception {}")
+                  .outdir(".")
+                  .run()
+                  .writeAll();
+                tb.deleteFiles("CompletionErrorMissing.class", "CompletionErrorIntfMissing.class", "CompletionErrorExcMissing.class");
+                // run javadoc:
+                if (Main.execute("javadoc", "CompletionError", CompletionError.class.getClassLoader(),
+                                 "-classpath", ".",
+                                 System.getProperty("test.src", ".") + File.separatorChar + "CompletionError.java") != 0)
+                    throw new Error();
+            }
+        }
+    }
+
+    private static int test;
+    private static boolean testsDone;
+
+    public static boolean start(com.sun.javadoc.RootDoc root) {
+        ClassDoc aux = root.classNamed("CompletionErrorAuxiliary");
+        if (aux == null)
+            throw new AssertionError("Cannot find CompletionErrorAuxiliary");
+
+        FieldDoc tf = findField(aux, "tf");
+        MethodDoc tm = findMethod(aux, "tm");
+        MethodDoc cm = findMethod(aux, "toString");
+        switch (test) {
+            case 0: aux.superclass(); break;
+            case 1: aux.superclassType(); break;
+            case 2: aux.interfaces(); break;
+            case 3: aux.interfaceTypes(); break;
+            case 4: if (tf != null) tf.type(); break;
+            case 5: if (tm != null) tm.overriddenClass(); break;
+            case 6: if (tm != null) tm.overriddenMethod(); break;
+            case 7: if (tm != null) tm.overriddenType(); break;
+            case 8:
+                if (tm != null) {
+                    for (Parameter p : tm.parameters()) {
+                        p.type();
+                    }
+                }
+                break;
+            case 9: if (tm != null) tm.receiverType(); break;
+            case 10: if (tm != null) tm.returnType(); break;
+            case 11: if (tm != null) tm.thrownExceptionTypes(); break;
+            case 12: if (tm != null) tm.thrownExceptions(); break;
+            case 13:
+                if (tm != null) {
+                    for (TypeVariable tv : tm.typeParameters()) {
+                        tv.bounds();
+                    }
+                }
+                break;
+            case 14: if (cm != null) cm.overriddenClass(); break;
+            case 15: if (cm != null) cm.overriddenMethod(); break;
+            case 16: if (cm != null) cm.overriddenType(); testsDone = true; break;
+            default:
+                throw new IllegalStateException("Unrecognized test!");
+        }
+        test++;
+        return true;
+    }
+
+    private static MethodDoc findMethod(ClassDoc cd, String name) {
+        for (MethodDoc m : cd.methods()) {
+            if (name.equals(m.name()))
+                return m;
+        }
+
+        return null;
+    }
+
+    private static FieldDoc findField(ClassDoc cd, String name) {
+        for (FieldDoc m : cd.fields()) {
+            if (name.equals(m.name()))
+                return m;
+        }
+
+        return null;
+    }
+}