8234896: Tab completion does not work for method references in jshell

Reviewed-by: rfield
This commit is contained in:
Jan Lahoda 2020-03-04 13:43:28 +01:00
parent 0c9983887d
commit e44dcf09c0
2 changed files with 92 additions and 14 deletions
src/jdk.jshell/share/classes/jdk/jshell
test/langtools/jdk/jshell

@ -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.ErroneousTree;
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 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
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 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
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)

@ -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 @@ public class CompletionSuggestionTest extends KullaTesting {
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();