diff options
author | George Fraser <george@fivetran.com> | 2019-01-06 18:25:28 -0800 |
---|---|---|
committer | George Fraser <george@fivetran.com> | 2019-01-06 18:25:28 -0800 |
commit | dda8cbf25f44af704098eb13147f749322369f58 (patch) | |
tree | 0e191eb88158db6d5252d91bd6275019ce47a0f5 | |
parent | ae21ddac94d7124e165af531b77c3a43eadfdbed (diff) | |
download | java-language-server-dda8cbf25f44af704098eb13147f749322369f58.zip |
Complete indirect superclasses
3 files changed, 78 insertions, 44 deletions
diff --git a/src/main/java/org/javacs/CompileFocus.java b/src/main/java/org/javacs/CompileFocus.java index 95369ad..c7875be 100644 --- a/src/main/java/org/javacs/CompileFocus.java +++ b/src/main/java/org/javacs/CompileFocus.java @@ -15,6 +15,7 @@ import javax.lang.model.element.*; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Types; public class CompileFocus { public static final int MAX_COMPLETION_ITEMS = 50; @@ -25,6 +26,7 @@ public class CompileFocus { private final int line, character; private final JavacTask task; private final Trees trees; + private final Types types; private final CompilationUnitTree root; private final TreePath path; @@ -36,6 +38,7 @@ public class CompileFocus { this.character = character; this.task = singleFileTask(parent, file, this.contents); this.trees = Trees.instance(task); + this.types = task.getTypes(); var profiler = new Profiler(); task.addTaskListener(profiler); @@ -296,46 +299,48 @@ public class CompileFocus { LOG.warning(String.format("`...%s` has not type", path.getLeaf())); return List.of(); } - if (hasMembers(type)) { - LOG.info(String.format("...completing virtual members of %s", type)); - - var result = new ArrayList<Completion>(); - var ts = supersWithSelf(type); - var alreadyAdded = new HashSet<String>(); - for (var t : ts) { - var e = types.asElement(t); - if (e != null) { - for (var member : e.getEnclosedElements()) { - // Don't add statics - if (member.getModifiers().contains(Modifier.STATIC)) continue; - // Don't add constructors - if (member.getSimpleName().contentEquals("<init>")) continue; - // Skip overridden members from superclass - if (alreadyAdded.contains(member.toString())) continue; - - // If type is a DeclaredType, check accessibility of member - if (type instanceof DeclaredType) { - if (trees.isAccessible(scope, member, (DeclaredType) type)) { - result.add(Completion.ofElement(member)); - } - } - // Otherwise, accessibility rules are very complicated - // Give up and just declare that everything is accessible - else result.add(Completion.ofElement(member)); - // Remember the signature of the added method, so we don't re-add it later + if (!hasMembers(type)) { + LOG.warning("...don't know how to complete members of type " + type); + return Collections.emptyList(); + } + + var result = new ArrayList<Completion>(); + var ts = supersWithSelf(type); + var alreadyAdded = new HashSet<String>(); + LOG.info(String.format("...completing virtual members of %s and %d supers", type, ts.size())); + for (var t : ts) { + var e = types.asElement(t); + if (e == null) { + LOG.warning(String.format("...can't convert supertype `%s` to element, skipping", t)); + continue; + } + for (var member : e.getEnclosedElements()) { + // Don't add statics + if (member.getModifiers().contains(Modifier.STATIC)) continue; + // Don't add constructors + if (member.getSimpleName().contentEquals("<init>")) continue; + // Skip overridden members from superclass + if (alreadyAdded.contains(member.toString())) continue; + + // If type is a DeclaredType, check accessibility of member + if (type instanceof DeclaredType) { + if (trees.isAccessible(scope, member, (DeclaredType) type)) { + result.add(Completion.ofElement(member)); alreadyAdded.add(member.toString()); } } - - if (t instanceof ArrayType) { - result.add(Completion.ofKeyword("length")); + // Otherwise, accessibility rules are very complicated + // Give up and just declare that everything is accessible + else { + result.add(Completion.ofElement(member)); + alreadyAdded.add(member.toString()); } } - return result; - } else { - LOG.warning("...don't know how to complete members of type " + type); - return Collections.emptyList(); } + if (type instanceof ArrayType) { + result.add(Completion.ofKeyword("length")); + } + return result; } } @@ -489,18 +494,20 @@ public class CompileFocus { return false; } - private List<TypeMirror> supersWithSelf(TypeMirror t) { - var elements = task.getElements(); - var types = task.getTypes(); - var result = new ArrayList<TypeMirror>(); - result.add(t); - // Add members of superclasses and interfaces - result.addAll(types.directSupertypes(t)); + private Set<TypeMirror> supersWithSelf(TypeMirror t) { + var types = new HashSet<TypeMirror>(); + collectSupers(t, types); // Object type is not included by default // We need to add it to get members like .equals(other) and .hashCode() - // TODO this may add things twice for interfaces with no super-interfaces - result.add(elements.getTypeElement("java.lang.Object").asType()); - return result; + types.add(task.getElements().getTypeElement("java.lang.Object").asType()); + return types; + } + + private void collectSupers(TypeMirror t, Set<TypeMirror> supers) { + supers.add(t); + for (var s : types.directSupertypes(t)) { + collectSupers(s, supers); + } } private boolean hasMembers(TypeMirror type) { diff --git a/src/test/java/org/javacs/CompletionsTest.java b/src/test/java/org/javacs/CompletionsTest.java index e00f6c4..4cef1fb 100644 --- a/src/test/java/org/javacs/CompletionsTest.java +++ b/src/test/java/org/javacs/CompletionsTest.java @@ -306,6 +306,16 @@ public class CompletionsTest extends CompletionsBase { assertThat(suggestions, hasItems("length")); } + @Test + public void indirectSuper() { + var file = "/org/javacs/example/CompleteIndirectSuper.java"; + + // a.? + var suggestions = insertText(file, 5, 14); + + assertThat(suggestions, hasItems("selfMethod", "super1Method", "super2Method")); + } + @Ignore // We are now managing imports with FixImports @Test public void addImport() { diff --git a/src/test/test-project/workspace/src/org/javacs/example/CompleteIndirectSuper.java b/src/test/test-project/workspace/src/org/javacs/example/CompleteIndirectSuper.java new file mode 100644 index 0000000..d26b437 --- /dev/null +++ b/src/test/test-project/workspace/src/org/javacs/example/CompleteIndirectSuper.java @@ -0,0 +1,17 @@ +package org.javacs.example; + +class CompleteIndirectSuper extends CompleteIndirectSuper1 { + static void test(CompleteIndirectSuper self) { + self. + } + + void selfMethod() { } +} + +class CompleteIndirectSuper1 extends CompleteIndirectSuper2 { + void super1Method() { } +} + +class CompleteIndirectSuper2 { + void super2Method() { } +}
\ No newline at end of file |