changeset 3064:5909f5a549ac

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
author jlahoda
date Mon, 28 Sep 2015 16:56:26 +0200
parents 1b3964a88c7d
children f55af75598d8
files src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java src/share/classes/com/sun/tools/javadoc/MethodDocImpl.java src/share/classes/com/sun/tools/javadoc/TypeMaker.java test/tools/javadoc/CompletionError.java
diffstat 4 files changed, 212 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java	Tue Oct 13 18:41:56 2015 +0300
+++ b/src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java	Mon Sep 28 16:56:26 2015 +0200
@@ -124,19 +124,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/src/share/classes/com/sun/tools/javadoc/MethodDocImpl.java	Tue Oct 13 18:41:56 2015 +0300
+++ b/src/share/classes/com/sun/tools/javadoc/MethodDocImpl.java	Mon Sep 28 16:56:26 2015 +0200
@@ -128,7 +128,7 @@
              t.hasTag(CLASS);
              t = env.types.supertype(t)) {
             ClassSymbol c = (ClassSymbol)t.tsym;
-            for (Scope.Entry e = c.members().lookup(sym.name); e.scope != null; e = e.next()) {
+            for (Scope.Entry e = membersOf(c).lookup(sym.name); e.scope != null; e = e.next()) {
                 if (sym.overrides(e.sym, 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 (Scope.Entry e = c.members().lookup(sym.name); e.scope != null; e = e.next()) {
+            for (Scope.Entry e = membersOf(c).lookup(sym.name); e.scope != null; e = e.next()) {
                 if (sym.overrides(e.sym, origin, env.types, true)) {
                     return env.getMethodDoc((MethodSymbol)e.sym);
                 }
@@ -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/src/share/classes/com/sun/tools/javadoc/TypeMaker.java	Tue Oct 13 18:41:56 2015 +0300
+++ b/src/share/classes/com/sun/tools/javadoc/TypeMaker.java	Mon Sep 28 16:56:26 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/test/tools/javadoc/CompletionError.java	Mon Sep 28 16:56:26 2015 +0200
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ * @modules jdk.javadoc
+ * @run main CompletionError
+ */
+
+import java.io.File;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.ToolProvider;
+
+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 {
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        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) {
+                List<JavaSource> fileObjects =
+                        Arrays.asList(new JavaSource("CompletionErrorAuxiliary", testSource.toString()),
+                                      new JavaSource("CompletionErrorMissing", "public class CompletionErrorMissing {}"),
+                                      new JavaSource("CompletionErrorIntfMissing", "public interface CompletionErrorIntfMissing {}"),
+                                      new JavaSource("CompletionErrorExcMissing", "public class CompletionErrorExcMissing extends Exception {}"));
+                Boolean result = compiler.getTask(null, null, null, Arrays.asList("-d", "."), null, fileObjects).call();
+                if (!result)
+                    throw new Error();
+                for (String delete : new String[] {"CompletionErrorMissing.class", "CompletionErrorIntfMissing.class", "CompletionErrorExcMissing.class"}) {
+                    Files.delete(Paths.get(delete));
+                }
+                // 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;
+    }
+
+    static class JavaSource extends SimpleJavaFileObject {
+        final String source;
+
+        public JavaSource(String name, String source) {
+            super(URI.create("myfo:/" + name + ".java"), JavaFileObject.Kind.SOURCE);
+            this.source = source;
+        }
+
+        @Override
+        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+            return source;
+        }
+    }
+}