# HG changeset patch # User shinyafox # Date 1476714191 -32400 # Node ID e8f487b79e24fd25b9cb9f71a127b4968cc5ee27 # Parent 265017792980e0b727798f7ad84a5bb7b9f5c910 8145263: JShell: Fix the format of SourceCodeAnalysis#documentation Reviewed-by: rfield, jlahoda diff -r 265017792980 -r e8f487b79e24 langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.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 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 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 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 typeParameters) { + return typeParameters.isEmpty() ? "" + : typeParameters.stream() + .map(tp -> elementHeader(at, tp)) + .collect(joining(", ", "<", ">")); + } @Override public String analyzeType(String code, int cursor) { diff -r 265017792980 -r e8f487b79e24 langtools/test/jdk/jshell/CompletionSuggestionTest.java --- 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(" 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 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 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 { A(T t) {} A(int i) {}}", + assertEval("class A { A(T a) {} A(int i) {} 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 a)", "A(int i)", " A(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 codeFacotry; + private final BiFunction evalFormatter; + private final BiFunction docFormatter; + int count; + + TestDocumentationWithGenerics( + Function codeFactory, + BiFunction evalFormatter, + BiFunction 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.assertDoc("T extends java.io.Serializable & CharSequence"); + t.assertDoc("K, D, M extends java.util.Map", + "K, D, M extends java.util.Map"); + }); } public void testVarArgs() {