8145263: JShell: Fix the format of SourceCodeAnalysis#documentation
authorshinyafox
Mon, 17 Oct 2016 23:23:11 +0900
changeset 41527 e8f487b79e24
parent 41526 265017792980
child 41528 6a4e580cac2c
8145263: JShell: Fix the format of SourceCodeAnalysis#documentation Reviewed-by: rfield, jlahoda
langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java
langtools/test/jdk/jshell/CompletionSuggestionTest.java
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java	Mon Oct 17 15:02:46 2016 +0100
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java	Mon Oct 17 23:23:11 2016 +0900
@@ -116,6 +116,7 @@
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.PackageElement;
 import javax.lang.model.element.QualifiedNameable;
+import javax.lang.model.element.TypeParameterElement;
 import javax.lang.model.element.VariableElement;
 import javax.lang.model.type.ArrayType;
 import javax.lang.model.type.ExecutableType;
@@ -132,6 +133,7 @@
 import static jdk.jshell.Util.REPL_DOESNOTMATTER_CLASS_NAME;
 import static java.util.stream.Collectors.joining;
 import static jdk.jshell.SourceCodeAnalysis.Completeness.DEFINITELY_INCOMPLETE;
+import static jdk.jshell.TreeDissector.printType;
 
 /**
  * The concrete implementation of SourceCodeAnalysis.
@@ -1185,7 +1187,7 @@
             proc.debug(ex, "SourceCodeAnalysisImpl.element2String(..., " + el + ")");
         }
 
-        return Util.expunge(elementHeader(el));
+        return Util.expunge(elementHeader(sourceCache.originalTask, el, !hasSyntheticParameterNames(el)));
     }
 
     private boolean hasSyntheticParameterNames(Element el) {
@@ -1248,7 +1250,7 @@
                 topLevelName2Signature2Method.put(binaryName, cache = createMethodCache(binaryName));
             }
 
-            String handle = elementHeader(method, false);
+            String handle = elementHeader(originalTask, method, false);
 
             return cache.getOrDefault(handle, method);
         }
@@ -1276,7 +1278,7 @@
                     Element currentMethod = trees.getElement(getCurrentPath());
 
                     if (currentMethod != null) {
-                        signature2Method.put(elementHeader(currentMethod, false), currentMethod);
+                        signature2Method.put(elementHeader(originalTask, currentMethod, false), currentMethod);
                     }
 
                     return null;
@@ -1331,39 +1333,79 @@
         return availableSources = result;
     }
 
-    private String elementHeader(Element el) {
-        return elementHeader(el, true);
+    private String elementHeader(AnalyzeTask at, Element el) {
+        return elementHeader(at, el, true);
     }
 
-    private String elementHeader(Element el, boolean includeParameterNames) {
+    private String elementHeader(AnalyzeTask at, Element el, boolean includeParameterNames) {
         switch (el.getKind()) {
-            case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE:
-                return ((TypeElement) el).getQualifiedName().toString();
+            case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE: {
+                TypeElement type = (TypeElement)el;
+                String fullname = type.getQualifiedName().toString();
+                Element pkg = at.getElements().getPackageOf(el);
+                String name = pkg == null ? fullname :
+                        proc.maps.fullClassNameAndPackageToClass(fullname, ((PackageElement)pkg).getQualifiedName().toString());
+
+                return name + typeParametersOpt(at, type.getTypeParameters());
+            }
+            case TYPE_PARAMETER: {
+                TypeParameterElement tp = (TypeParameterElement)el;
+                String name = tp.getSimpleName().toString();
+
+                List<? extends TypeMirror> bounds = tp.getBounds();
+                boolean boundIsObject = bounds.isEmpty() ||
+                        bounds.size() == 1 && at.getTypes().isSameType(bounds.get(0), Symtab.instance(at.getContext()).objectType);
+
+                return boundIsObject
+                        ? name
+                        : name + " extends " + bounds.stream()
+                                .map(bound -> printType(at, proc, bound))
+                                .collect(joining(" & "));
+            }
             case FIELD:
-                return elementHeader(el.getEnclosingElement()) + "." + el.getSimpleName() + ":" + el.asType();
+                return elementHeader(at, el.getEnclosingElement()) + "." + el.getSimpleName() + ":" + el.asType();
             case ENUM_CONSTANT:
-                return elementHeader(el.getEnclosingElement()) + "." + el.getSimpleName();
+                return elementHeader(at, el.getEnclosingElement()) + "." + el.getSimpleName();
             case EXCEPTION_PARAMETER: case LOCAL_VARIABLE: case PARAMETER: case RESOURCE_VARIABLE:
                 return el.getSimpleName() + ":" + el.asType();
-            case CONSTRUCTOR: case METHOD:
+            case CONSTRUCTOR: case METHOD: {
                 StringBuilder header = new StringBuilder();
-                header.append(elementHeader(el.getEnclosingElement()));
-                if (el.getKind() == ElementKind.METHOD) {
-                    header.append(".");
-                    header.append(el.getSimpleName());
+
+                boolean isMethod = el.getKind() == ElementKind.METHOD;
+                ExecutableElement method = (ExecutableElement) el;
+
+                if (isMethod) {
+                    // return type
+                    header.append(printType(at, proc, method.getReturnType())).append(" ");
+                } else {
+                    // type parameters for the constructor
+                    String typeParameters = typeParametersOpt(at, method.getTypeParameters());
+                    if (!typeParameters.isEmpty()) {
+                        header.append(typeParameters).append(" ");
+                    }
                 }
+
+                // receiver type
+                String clazz = elementHeader(at, el.getEnclosingElement());
+                header.append(clazz);
+
+                if (isMethod) {
+                    //method name with type parameters
+                    (clazz.isEmpty() ? header : header.append("."))
+                            .append(typeParametersOpt(at, method.getTypeParameters()))
+                            .append(el.getSimpleName());
+                }
+
+                // arguments
                 header.append("(");
                 String sep = "";
-                ExecutableElement method = (ExecutableElement) el;
                 for (Iterator<? extends VariableElement> i = method.getParameters().iterator(); i.hasNext();) {
                     VariableElement p = i.next();
                     header.append(sep);
                     if (!i.hasNext() && method.isVarArgs()) {
-                        header.append(unwrapArrayType(p.asType()));
-                        header.append("...");
-
+                        header.append(printType(at, proc, unwrapArrayType(p.asType()))).append("...");
                     } else {
-                        header.append(p.asType());
+                        header.append(printType(at, proc, p.asType()));
                     }
                     if (includeParameterNames) {
                         header.append(" ");
@@ -1372,8 +1414,18 @@
                     sep = ", ";
                 }
                 header.append(")");
+
+                // throws
+                List<? extends TypeMirror> thrownTypes = method.getThrownTypes();
+                if (!thrownTypes.isEmpty()) {
+                    header.append(" throws ")
+                            .append(thrownTypes.stream()
+                                    .map(type -> printType(at, proc, type))
+                                    .collect(joining(", ")));
+                }
                 return header.toString();
-           default:
+            }
+            default:
                 return el.toString();
         }
     }
@@ -1383,6 +1435,12 @@
         }
         return arrayType;
     }
+    private String typeParametersOpt(AnalyzeTask at, List<? extends TypeParameterElement> typeParameters) {
+        return typeParameters.isEmpty() ? ""
+                : typeParameters.stream()
+                        .map(tp -> elementHeader(at, tp))
+                        .collect(joining(", ", "<", ">"));
+    }
 
     @Override
     public String analyzeType(String code, int cursor) {
--- a/langtools/test/jdk/jshell/CompletionSuggestionTest.java	Mon Oct 17 15:02:46 2016 +0100
+++ b/langtools/test/jdk/jshell/CompletionSuggestionTest.java	Mon Oct 17 23:23:11 2016 +0900
@@ -23,8 +23,8 @@
 
 /*
  * @test
- * @bug 8131025 8141092 8153761
- * @summary Test Completion
+ * @bug 8131025 8141092 8153761 8145263
+ * @summary Test Completion and Documentation
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.main
  *          jdk.jdeps/com.sun.tools.javap
@@ -43,6 +43,8 @@
 import java.util.Collections;
 import java.util.Set;
 import java.util.HashSet;
+import java.util.function.BiFunction;
+import java.util.function.Function;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
 
@@ -304,35 +306,35 @@
     public void testDocumentation() throws Exception {
         dontReadParameterNamesFromClassFile();
         assertDocumentation("System.getProperty(|",
-                "java.lang.System.getProperty(java.lang.String key)",
-                "java.lang.System.getProperty(java.lang.String key, java.lang.String def)");
+                "String System.getProperty(String key)",
+                "String System.getProperty(String key, String def)");
         assertEval("char[] chars = null;");
         assertDocumentation("new String(chars, |",
-                "java.lang.String(char[] arg0, int arg1, int arg2)");
+                "String(char[], int, int)");
         assertDocumentation("String.format(|",
-                "java.lang.String.format(java.lang.String arg0, java.lang.Object... arg1)",
-                "java.lang.String.format(java.util.Locale arg0, java.lang.String arg1, java.lang.Object... arg2)");
-        assertDocumentation("\"\".getBytes(\"\"|", "java.lang.String.getBytes(int arg0, int arg1, byte[] arg2, int arg3)",
-                                                    "java.lang.String.getBytes(java.lang.String arg0)",
-                                                    "java.lang.String.getBytes(java.nio.charset.Charset arg0)");
-        assertDocumentation("\"\".getBytes(\"\" |", "java.lang.String.getBytes(int arg0, int arg1, byte[] arg2, int arg3)",
-                                                     "java.lang.String.getBytes(java.lang.String arg0)",
-                                                     "java.lang.String.getBytes(java.nio.charset.Charset arg0)");
+                "String String.format(String, Object...)",
+                "String String.format(java.util.Locale, String, Object...)");
+        assertDocumentation("\"\".getBytes(\"\"|", "void String.getBytes(int, int, byte[], int)",
+                                                    "byte[] String.getBytes(String) throws java.io.UnsupportedEncodingException",
+                                                    "byte[] String.getBytes(java.nio.charset.Charset)");
+        assertDocumentation("\"\".getBytes(\"\" |", "void String.getBytes(int, int, byte[], int)",
+                                                     "byte[] String.getBytes(String) throws java.io.UnsupportedEncodingException",
+                                                     "byte[] String.getBytes(java.nio.charset.Charset)");
     }
 
     public void testMethodsWithNoArguments() throws Exception {
         dontReadParameterNamesFromClassFile();
         assertDocumentation("System.out.println(|",
-                "java.io.PrintStream.println()",
-                "java.io.PrintStream.println(boolean arg0)",
-                "java.io.PrintStream.println(char arg0)",
-                "java.io.PrintStream.println(int arg0)",
-                "java.io.PrintStream.println(long arg0)",
-                "java.io.PrintStream.println(float arg0)",
-                "java.io.PrintStream.println(double arg0)",
-                "java.io.PrintStream.println(char[] arg0)",
-                "java.io.PrintStream.println(java.lang.String arg0)",
-                "java.io.PrintStream.println(java.lang.Object arg0)");
+                "void java.io.PrintStream.println()",
+                "void java.io.PrintStream.println(boolean)",
+                "void java.io.PrintStream.println(char)",
+                "void java.io.PrintStream.println(int)",
+                "void java.io.PrintStream.println(long)",
+                "void java.io.PrintStream.println(float)",
+                "void java.io.PrintStream.println(double)",
+                "void java.io.PrintStream.println(char[])",
+                "void java.io.PrintStream.println(String)",
+                "void java.io.PrintStream.println(Object)");
     }
 
     public void testErroneous() {
@@ -472,14 +474,14 @@
 
     public void testDocumentationOfUserDefinedMethods() {
         assertEval("void f() {}");
-        assertDocumentation("f(|", "f()");
+        assertDocumentation("f(|", "void f()");
         assertEval("void f(int i) {}");
-        assertDocumentation("f(|", "f()", "f(int i)");
+        assertDocumentation("f(|", "void f()", "void f(int i)");
         assertEval("<T> void f(T... ts) {}", DiagCheck.DIAG_WARNING, DiagCheck.DIAG_OK);
-        assertDocumentation("f(|", "f()", "f(int i)", "f(T... ts)");
+        assertDocumentation("f(|", "void f()", "void f(int i)", "void <T>f(T... ts)");
         assertEval("class A {}");
         assertEval("void f(A a) {}");
-        assertDocumentation("f(|", "f()", "f(int i)", "f(T... ts)", "f(A a)");
+        assertDocumentation("f(|", "void f()", "void f(int i)", "void <T>f(T... ts)", "void f(A a)");
     }
 
     public void testDocumentationOfUserDefinedConstructors() {
@@ -489,25 +491,25 @@
                 ste(MAIN_SNIPPET, VALID, VALID, true, null),
                 ste(a, VALID, OVERWRITTEN, false, MAIN_SNIPPET)));
         assertDocumentation("new A(|", "A()", "A(int i)");
-        assertEval("class A<T> { A(T t) {} A(int i) {}}",
+        assertEval("class A<T> { A(T a) {} A(int i) {} <U> A(T t, U u) {}}",
                 ste(MAIN_SNIPPET, VALID, VALID, true, null),
                 ste(a2, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
-        assertDocumentation("new A(|", "A(T t)", "A(int i)");
+        assertDocumentation("new A(|", "A<T>(T a)", "A<T>(int i)", "<U> A<T>(T t, U u)");
     }
 
     public void testDocumentationOfOverriddenMethods() throws Exception {
         dontReadParameterNamesFromClassFile();
         assertDocumentation("\"\".wait(|",
-            "java.lang.Object.wait(long arg0)",
-            "java.lang.Object.wait(long arg0, int arg1)",
-            "java.lang.Object.wait()");
+            "void Object.wait(long) throws InterruptedException",
+            "void Object.wait(long, int) throws InterruptedException",
+            "void Object.wait() throws InterruptedException");
         assertEval("class Base {void method() {}}");
         Snippet e = classKey(assertEval("class Extend extends Base {}"));
-        assertDocumentation("new Extend().method(|", "Base.method()");
+        assertDocumentation("new Extend().method(|", "void Base.method()");
         assertEval("class Extend extends Base {void method() {}}",
                 ste(MAIN_SNIPPET, VALID, VALID, true, null),
                 ste(e, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
-        assertDocumentation("new Extend().method(|", "Extend.method()");
+        assertDocumentation("new Extend().method(|", "void Extend.method()");
     }
 
     public void testDocumentationOfInvisibleMethods() {
@@ -534,13 +536,67 @@
         assertEval("void method(int n, Object o) { }");
         assertEval("void method(Object n, int o) { }");
         assertDocumentation("method(primitive,|",
-                "method(int n, java.lang.Object o)",
-                "method(java.lang.Object n, int o)");
+                "void method(int n, Object o)",
+                "void method(Object n, int o)");
         assertDocumentation("method(boxed,|",
-                "method(int n, java.lang.Object o)",
-                "method(java.lang.Object n, int o)");
+                "void method(int n, Object o)",
+                "void method(Object n, int o)");
         assertDocumentation("method(object,|",
-                "method(java.lang.Object n, int o)");
+                "void method(Object n, int o)");
+    }
+
+    public void testDocumentationWithGenerics() {
+        class TestDocumentationWithGenerics {
+            private final Function<Integer, String> codeFacotry;
+            private final BiFunction<String, Integer, String> evalFormatter;
+            private final BiFunction<String, Integer, String> docFormatter;
+            int count;
+
+            TestDocumentationWithGenerics(
+                    Function<Integer, String> codeFactory,
+                    BiFunction<String, Integer, String> evalFormatter,
+                    BiFunction<String, Integer, String> documentationFormatter) {
+                this.codeFacotry = codeFactory;
+                this.evalFormatter = evalFormatter;
+                this.docFormatter = documentationFormatter;
+            }
+
+            void assertDoc(String generics) {
+                assertDoc(generics, generics);
+            }
+
+            void assertDoc(String generics, String expectedGenerics) {
+                assertEval(evalFormatter.apply(generics, count));
+                assertDocumentation(codeFacotry.apply(count), docFormatter.apply(expectedGenerics, count));
+                count++;
+            }
+        }
+
+        TestDocumentationWithGenerics[] tests = {
+            new TestDocumentationWithGenerics(
+                    i -> "f" + i + "(|",
+                    (g, i) -> "<" + g + "> void f" + i + "() {}",
+                    (g, i) -> "void <" + g + ">f" + i + "()"
+            ),
+            new TestDocumentationWithGenerics(
+                    i -> "new C" + i + "().f(|",
+                    (g, i) -> "class C" + i + "<" + g + "> { void f() {} }",
+                    (g, i) -> "void C" + i + "<" + g + ">.f()"
+            )
+        };
+
+        Arrays.stream(tests).forEach(t -> {
+                t.assertDoc("T");
+                t.assertDoc("T extends Object",
+                        "T");
+                t.assertDoc("T extends String");
+                t.assertDoc("T extends java.lang.String",
+                        "T extends String");
+                t.assertDoc("T extends Number & Comparable<T>");
+                t.assertDoc("T extends java.io.Serializable & CharSequence");
+                t.assertDoc("K, D, M extends java.util.Map<K, D>",
+                        "K, D, M extends java.util.Map<K,D>");
+        });
     }
 
     public void testVarArgs() {