summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Fraser <george@fivetran.com>2019-01-06 18:25:28 -0800
committerGeorge Fraser <george@fivetran.com>2019-01-06 18:25:28 -0800
commitdda8cbf25f44af704098eb13147f749322369f58 (patch)
tree0e191eb88158db6d5252d91bd6275019ce47a0f5
parentae21ddac94d7124e165af531b77c3a43eadfdbed (diff)
downloadjava-language-server-dda8cbf25f44af704098eb13147f749322369f58.zip
Complete indirect superclasses
-rw-r--r--src/main/java/org/javacs/CompileFocus.java95
-rw-r--r--src/test/java/org/javacs/CompletionsTest.java10
-rw-r--r--src/test/test-project/workspace/src/org/javacs/example/CompleteIndirectSuper.java17
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