changeset 60305:1c06a8ee8aca jdk-15+13

8234896: Tab completion does not work for method references in jshell. Reviewed-by: rfield
author jlahoda
date Wed, 04 Mar 2020 13:43:28 +0100
parents d2fc63de7876
children b0d94da54415
files src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java test/langtools/jdk/jshell/CompletionSuggestionTest.java
diffstat 2 files changed, 92 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java	Wed Mar 04 13:43:27 2020 +0100
+++ b/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java	Wed Mar 04 13:43:28 2020 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2020, 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
@@ -32,6 +32,7 @@
 import com.sun.source.tree.ExpressionTree;
 import com.sun.source.tree.IdentifierTree;
 import com.sun.source.tree.ImportTree;
+import com.sun.source.tree.MemberReferenceTree;
 import com.sun.source.tree.MemberSelectTree;
 import com.sun.source.tree.MethodInvocationTree;
 import com.sun.source.tree.MethodTree;
@@ -309,11 +310,53 @@
                 Predicate<Element> smartFilter;
                 Iterable<TypeMirror> targetTypes = findTargetType(at, tp);
                 if (targetTypes != null) {
-                    smartTypeFilter = el -> {
-                        TypeMirror resultOf = resultTypeOf(el);
-                        return Util.stream(targetTypes)
-                                .anyMatch(targetType -> at.getTypes().isAssignable(resultOf, targetType));
-                    };
+                    if (tp.getLeaf().getKind() == Kind.MEMBER_REFERENCE) {
+                        Types types = at.getTypes();
+                        smartTypeFilter = t -> {
+                            if (t.getKind() != ElementKind.METHOD) {
+                                return false;
+                            }
+                            ExecutableElement ee = (ExecutableElement) t;
+                            for (TypeMirror type : targetTypes) {
+                                if (type.getKind() != TypeKind.DECLARED)
+                                    continue;
+                                DeclaredType d = (DeclaredType) type;
+                                List<? extends Element> enclosed =
+                                        ((TypeElement) d.asElement()).getEnclosedElements();
+                                for (ExecutableElement m : ElementFilter.methodsIn(enclosed)) {
+                                    boolean matches = true;
+                                    if (!m.getModifiers().contains(Modifier.ABSTRACT)) {
+                                        continue;
+                                    }
+                                    if (m.getParameters().size() != ee.getParameters().size()) {
+                                        continue;
+                                    }
+                                    ExecutableType mInst = (ExecutableType) types.asMemberOf(d, m);
+                                    List<? extends TypeMirror> expectedParams = mInst.getParameterTypes();
+                                    if (mInst.getReturnType().getKind() != TypeKind.VOID &&
+                                        !types.isSubtype(ee.getReturnType(), mInst.getReturnType())) {
+                                        continue;
+                                    }
+                                    for (int i = 0; i < m.getParameters().size(); i++) {
+                                        if (!types.isSubtype(expectedParams.get(i),
+                                                             ee.getParameters().get(i).asType())) {
+                                            matches = false;
+                                        }
+                                    }
+                                    if (matches) {
+                                        return true;
+                                    }
+                                }
+                            }
+                            return false;
+                        };
+                    } else {
+                        smartTypeFilter = el -> {
+                            TypeMirror resultOf = resultTypeOf(el);
+                            return Util.stream(targetTypes)
+                                    .anyMatch(targetType -> at.getTypes().isAssignable(resultOf, targetType));
+                        };
+                    }
 
                     smartFilter = IS_CLASS.negate()
                                           .and(IS_INTERFACE.negate())
@@ -324,19 +367,31 @@
                     smartTypeFilter = TRUE;
                 }
                 switch (tp.getLeaf().getKind()) {
-                    case MEMBER_SELECT: {
-                        MemberSelectTree mst = (MemberSelectTree)tp.getLeaf();
-                        if (mst.getIdentifier().contentEquals("*"))
+                    case MEMBER_REFERENCE, MEMBER_SELECT: {
+                        javax.lang.model.element.Name identifier;
+                        ExpressionTree expression;
+                        Function<Boolean, String> paren;
+                        if (tp.getLeaf().getKind() == Kind.MEMBER_SELECT) {
+                            MemberSelectTree mst = (MemberSelectTree)tp.getLeaf();
+                            identifier = mst.getIdentifier();
+                            expression = mst.getExpression();
+                            paren = DEFAULT_PAREN;
+                        } else {
+                            MemberReferenceTree mst = (MemberReferenceTree)tp.getLeaf();
+                            identifier = mst.getName();
+                            expression = mst.getQualifierExpression();
+                            paren = NO_PAREN;
+                        }
+                        if (identifier.contentEquals("*"))
                             break;
-                        TreePath exprPath = new TreePath(tp, mst.getExpression());
+                        TreePath exprPath = new TreePath(tp, expression);
                         TypeMirror site = at.trees().getTypeMirror(exprPath);
                         boolean staticOnly = isStaticContext(at, exprPath);
                         ImportTree it = findImport(tp);
                         boolean isImport = it != null;
 
-                        List<? extends Element> members = membersOf(at, site, staticOnly && !isImport);
+                        List<? extends Element> members = membersOf(at, site, staticOnly && !isImport && tp.getLeaf().getKind() == Kind.MEMBER_SELECT);
                         Predicate<Element> filter = accessibility;
-                        Function<Boolean, String> paren = DEFAULT_PAREN;
 
                         if (isNewClass(tp)) { // new xxx.|
                             Predicate<Element> constructorFilter = accessibility.and(IS_CONSTRUCTOR)
--- a/test/langtools/jdk/jshell/CompletionSuggestionTest.java	Wed Mar 04 13:43:27 2020 +0100
+++ b/test/langtools/jdk/jshell/CompletionSuggestionTest.java	Wed Mar 04 13:43:28 2020 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2020, 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
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8131025 8141092 8153761 8145263 8131019 8175886 8176184 8176241 8176110 8177466 8197439 8221759
+ * @bug 8131025 8141092 8153761 8145263 8131019 8175886 8176184 8176241 8176110 8177466 8197439 8221759 8234896
  * @summary Test Completion and Documentation
  * @library /tools/lib
  * @modules jdk.compiler/com.sun.tools.javac.api
@@ -675,6 +675,29 @@
         assertCompletionIncludesExcludes("new Undefined() { int i = \"\".l|", Set.of("length()"), Set.of());
     }
 
+    public void testMemberReferences() {
+        assertEval("class C {" +
+                   "    public static String stat() { return null; }" +
+                   "    public static void statVoid(String s) {}" +
+                   "    public static Integer statConvert1(String s) { return null; }" +
+                   "    public static String statConvert2(Integer s) { return null; }" +
+                   "    public static String statConvert3(CharSequence s) { return null; }" +
+                   "    public String inst() { return null; }" +
+                   "    public void instVoid(String s) { }" +
+                   "}");
+        assertEval("interface FI { public void t(String s); }");
+        assertCompletion("FI fi = C::|", (Boolean) null, "stat", "statConvert1", "statConvert2", "statConvert3", "statVoid");
+        assertCompletion("FI fi = C::|", true, "statConvert1", "statConvert3","statVoid");
+        assertCompletion("FI fi = new C()::i|", (Boolean) null, "inst", "instVoid");
+        assertCompletion("FI fi = new C()::i|", true, "instVoid");
+        assertEval("interface FI2<R, P> { public R t(P p); }");
+        assertCompletion("FI2<String, Integer> fi = C::|", (Boolean) null, "stat", "statConvert1", "statConvert2", "statConvert3", "statVoid");
+        assertCompletion("FI2<String, Integer> fi = C::|", true, "statConvert2");
+        assertCompletion("FI2<String, CharSequence> fi = C::|", true, "statConvert3");
+        assertCompletion("FI2<String, String> fi = C::|", true, "statConvert3");
+        assertCompletion("FI2<Object, String> fi = C::|", true, "statConvert1", "statConvert3");
+    }
+
     @BeforeMethod
     public void setUp() {
         super.setUp();