# HG changeset patch # User jlahoda # Date 1471242506 -7200 # Node ID 3f7eb1205cee9be6f06d4362027e0be220450981 # Parent 78115e0f10f35393d28dbcc7fd9cde60ab2522b0 8129421: JShell: unacceptable suggestions in 'extends', 'implements' in smart completion 8129422: JShell: methods and fields of uncompleted expressions should be suggested Summary: Fixing several completion bugs Reviewed-by: rfield diff -r 78115e0f10f3 -r 3f7eb1205cee langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java --- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java Sat Aug 13 09:42:26 2016 -0700 +++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java Mon Aug 15 08:28:26 2016 +0200 @@ -27,6 +27,7 @@ import jdk.jshell.SourceCodeAnalysis.Completeness; import com.sun.source.tree.AssignmentTree; +import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.ErroneousTree; import com.sun.source.tree.ExpressionTree; @@ -39,6 +40,7 @@ import com.sun.source.tree.Scope; import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; +import com.sun.source.tree.TypeParameterTree; import com.sun.source.tree.VariableTree; import com.sun.source.util.JavacTask; import com.sun.source.util.SourcePositions; @@ -91,6 +93,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Comparator; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; @@ -267,6 +270,7 @@ case IMPORT: codeWrap = proc.outerMap.wrapImport(Wrap.simpleWrap(code + "any.any"), null); break; + case CLASS: case METHOD: codeWrap = proc.outerMap.wrapInTrialClass(Wrap.classMemberWrap(code)); break; @@ -380,10 +384,46 @@ addElements(membersOf(at, at.getElements().getPackageElement("").asType(), false), it.isStatic() ? STATIC_ONLY.and(accessibility) : accessibility, smartFilter, result); } break; - case ERRONEOUS: - case EMPTY_STATEMENT: { + case CLASS: { + Predicate accept = accessibility.and(IS_TYPE); + addScopeElements(at, scope, IDENTITY, accept, smartFilter, result); + addElements(primitivesOrVoid(at), TRUE, smartFilter, result); + break; + } + case BLOCK: + case EMPTY_STATEMENT: + case ERRONEOUS: { boolean staticOnly = ReplResolve.isStatic(((JavacScope)scope).getEnv()); Predicate accept = accessibility.and(staticOnly ? STATIC_ONLY : TRUE); + if (isClass(tp)) { + ClassTree clazz = (ClassTree) tp.getParentPath().getLeaf(); + if (clazz.getExtendsClause() == tp.getLeaf()) { + accept = accept.and(IS_TYPE); + smartFilter = smartFilter.and(el -> el.getKind() == ElementKind.CLASS); + } else { + Predicate f = smartFilterFromList(at, tp, clazz.getImplementsClause(), tp.getLeaf()); + if (f != null) { + accept = accept.and(IS_TYPE); + smartFilter = f.and(el -> el.getKind() == ElementKind.INTERFACE); + } + } + } else if (isTypeParameter(tp)) { + TypeParameterTree tpt = (TypeParameterTree) tp.getParentPath().getLeaf(); + Predicate f = smartFilterFromList(at, tp, tpt.getBounds(), tp.getLeaf()); + if (f != null) { + accept = accept.and(IS_TYPE); + smartFilter = f; + if (!tpt.getBounds().isEmpty() && tpt.getBounds().get(0) != tp.getLeaf()) { + smartFilter = smartFilter.and(el -> el.getKind() == ElementKind.INTERFACE); + } + } + } else if (isVariable(tp)) { + VariableTree var = (VariableTree) tp.getParentPath().getLeaf(); + if (var.getType() == tp.getLeaf()) { + accept = accept.and(IS_TYPE); + } + } + addScopeElements(at, scope, IDENTITY, accept, smartFilter, result); Tree parent = tp.getParentPath().getLeaf(); @@ -413,6 +453,23 @@ return result; } + private static final Set CLASS_KINDS = EnumSet.of( + Kind.ANNOTATION_TYPE, Kind.CLASS, Kind.ENUM, Kind.INTERFACE + ); + + private Predicate smartFilterFromList(AnalyzeTask at, TreePath base, Collection types, Tree current) { + Set existingEls = new HashSet<>(); + + for (Tree type : types) { + if (type == current) { + return el -> !existingEls.contains(el); + } + existingEls.add(at.trees().getElement(new TreePath(base, type))); + } + + return null; + } + @Override public SnippetWrapper wrapper(Snippet snippet) { return new SnippetWrapper() { @@ -516,6 +573,21 @@ ((MethodTree)parent).getThrows().contains(tp.getLeaf()); } + private boolean isClass(TreePath tp) { + return tp.getParentPath() != null && + CLASS_KINDS.contains(tp.getParentPath().getLeaf().getKind()); + } + + private boolean isTypeParameter(TreePath tp) { + return tp.getParentPath() != null && + tp.getParentPath().getLeaf().getKind() == Kind.TYPE_PARAMETER; + } + + private boolean isVariable(TreePath tp) { + return tp.getParentPath() != null && + tp.getParentPath().getLeaf().getKind() == Kind.VARIABLE; + } + private ImportTree findImport(TreePath tp) { while (tp != null && tp.getLeaf().getKind() != Kind.IMPORT) { tp = tp.getParentPath(); @@ -550,6 +622,7 @@ private final Predicate IS_PACKAGE = el -> el.getKind() == ElementKind.PACKAGE; private final Predicate IS_CLASS = el -> el.getKind().isClass(); private final Predicate IS_INTERFACE = el -> el.getKind().isInterface(); + private final Predicate IS_TYPE = IS_CLASS.or(IS_INTERFACE).or(el -> el.getKind() == ElementKind.TYPE_PARAMETER); private final Predicate IS_VOID = el -> el.asType().getKind() == TypeKind.VOID; private final Predicate STATIC_ONLY = el -> { ElementKind kind = el.getKind(); @@ -583,6 +656,11 @@ for (Element c : elements) { if (!accept.test(c)) continue; + if (c.getKind() == ElementKind.METHOD && + c.getSimpleName().contentEquals(Util.DOIT_METHOD_NAME) && + ((ExecutableElement) c).getParameters().isEmpty()) { + continue; + } String simpleName = simpleName(c); if (c.getKind() == ElementKind.CONSTRUCTOR || c.getKind() == ElementKind.METHOD) { simpleName += paren.apply(hasParams.contains(simpleName)); @@ -754,13 +832,25 @@ }; @SuppressWarnings("unchecked") List result = Util.stream(scopeIterable) - .flatMap(s -> Util.stream((Iterable)s.getLocalElements())) + .flatMap(s -> localElements(s)) .flatMap(el -> Util.stream((Iterable)elementConvertor.apply(el))) .collect(toCollection(ArrayList :: new)); result.addAll(listPackages(at, "")); return result; } + private Stream localElements(Scope scope) { + @SuppressWarnings("unchecked") + Stream elements = Util.stream((Iterable)scope.getLocalElements()); + + if (scope.getEnclosingScope() != null && + scope.getEnclosingClass() != scope.getEnclosingScope().getEnclosingClass()) { + elements = Stream.concat(elements, scope.getEnclosingClass().getEnclosedElements().stream()); + } + + return elements; + } + @SuppressWarnings("fallthrough") private Iterable findTargetType(AnalyzeTask at, TreePath forPath) { if (forPath.getParentPath() == null) diff -r 78115e0f10f3 -r 3f7eb1205cee langtools/test/jdk/jshell/CompletionSuggestionTest.java --- a/langtools/test/jdk/jshell/CompletionSuggestionTest.java Sat Aug 13 09:42:26 2016 -0700 +++ b/langtools/test/jdk/jshell/CompletionSuggestionTest.java Mon Aug 15 08:28:26 2016 +0200 @@ -415,7 +415,7 @@ assertCompletion("new Clazz() {}.defaultM|", "defaultMethod()"); } - @Test(enabled = false) // TODO 8129422 + @Test public void testUncompletedDeclaration() { assertCompletion("class Clazz { Claz|", "Clazz"); assertCompletion("class Clazz { class A extends Claz|", "Clazz"); @@ -423,16 +423,18 @@ assertCompletion("class Clazz { static Clazz clazz; Object o = cla|", "clazz"); assertCompletion("class Clazz { Clazz clazz; static Object o = cla|", true); assertCompletion("class Clazz { void method(Claz|", "Clazz"); - assertCompletion("class A { int method() { return 0; } int a = meth|", "method"); + assertCompletion("class A { int method() { return 0; } int a = meth|", "method()"); assertCompletion("class A { int field = 0; int method() { return fiel|", "field"); - assertCompletion("class A { static int method() { return 0; } int a = meth|", "method"); + assertCompletion("class A { static int method() { return 0; } int a = meth|", "method()"); assertCompletion("class A { static int field = 0; int method() { return fiel|", "field"); assertCompletion("class A { int method() { return 0; } static int a = meth|", true); assertCompletion("class A { int field = 0; static int method() { return fiel|", true); } - @Test(enabled = false) // TODO 8129421 + @Test public void testClassDeclaration() { + assertEval("void ClazzM() {}"); + assertEval("void InterfaceM() {}"); assertEval("interface Interface {}"); assertCompletion("interface A extends Interf|", "Interface"); assertCompletion("class A implements Interf|", "Interface"); @@ -445,6 +447,27 @@ assertCompletion("interface A implements Inter|"); assertCompletion("class A implements Claz|", true); assertCompletion("class A extends Clazz implements Interface, Interf|", true, "Interface1"); + assertCompletion("class A extends Clazz implements Interface, Interf|", true, "Interface1"); + assertEval("class InterfaceClazz {}"); + assertCompletion("class A