summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/javacs/FixImports.java6
-rw-r--r--src/main/java/org/javacs/JavaCompilerService.java59
-rw-r--r--src/test/java/org/javacs/JavaCompilerServiceTest.java11
-rw-r--r--src/test/resources/MissingImport.java5
4 files changed, 73 insertions, 8 deletions
diff --git a/src/main/java/org/javacs/FixImports.java b/src/main/java/org/javacs/FixImports.java
index 50d531d..1a183ba 100644
--- a/src/main/java/org/javacs/FixImports.java
+++ b/src/main/java/org/javacs/FixImports.java
@@ -19,12 +19,6 @@ class FixImports {
this.classPath = classPath;
}
- /** Find all unresolved symbols that start with an uppercase letter */
- Set<String> unresolvedSymbols(CompilationUnitTree tree) {
- // TODO
- return Collections.emptySet();
- }
-
/** Find all already-imported symbols in all .java files in sourcePath */
ExistingImports existingImports(Collection<Path> sourcePath) {
Set<String> classes = new HashSet<>(), packages = new HashSet<>();
diff --git a/src/main/java/org/javacs/JavaCompilerService.java b/src/main/java/org/javacs/JavaCompilerService.java
index 82662be..5c0d2be 100644
--- a/src/main/java/org/javacs/JavaCompilerService.java
+++ b/src/main/java/org/javacs/JavaCompilerService.java
@@ -817,7 +817,66 @@ public class JavaCompilerService {
return sourcePath.stream().flatMap(dir -> Parser.findSymbols(dir, query));
}
+ // TODO this is ugly, suggests something needs to be moved into JavaCompilerService
public Trees trees() {
return Trees.instance(cache.task);
}
+
+ /**
+ * Figure out what imports this file should have. Star-imports like `import java.util.*` are converted to individual
+ * class imports. Missing imports are inferred by looking at imports in other source files.
+ */
+ public Set<String> fixImports(URI file, String contents) {
+ // Compile a single file
+ JavacTask task = singleFileTask(file, contents);
+ CompilationUnitTree tree;
+ try {
+ tree = task.parse().iterator().next();
+ task.analyze();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ // Check diagnostics for missing imports
+ Set<String> unresolved = new HashSet<>();
+ for (Diagnostic<? extends JavaFileObject> d : diags) {
+ if (d.getCode().equals("compiler.err.cant.resolve.location") && d.getSource().toUri().equals(file)) {
+ long start = d.getStartPosition(), end = d.getEndPosition();
+ String id = contents.substring((int) start, (int) end);
+ if (id.matches("[A-Z]\\w+")) {
+ unresolved.add(id);
+ }
+ }
+ }
+ // Look at imports in other classes to help us guess how to fix imports
+ FixImports fix = new FixImports(classes);
+ FixImports.ExistingImports sourcePathImports = fix.existingImports(sourcePath);
+ Map<String, String> fixes = fix.resolveSymbols(unresolved, sourcePathImports);
+ // Figure out which existing imports are actually used
+ Trees trees = Trees.instance(task);
+ Set<String> qualifiedNames = new HashSet<>();
+ class FindUsedImports extends TreePathScanner<Void, Void> {
+ @Override
+ public Void visitIdentifier(IdentifierTree node, Void nothing) {
+ Element e = trees.getElement(getCurrentPath());
+ if (e instanceof TypeElement) {
+ TypeElement t = (TypeElement) e;
+ String qualifiedName = t.getQualifiedName().toString();
+ int lastDot = qualifiedName.lastIndexOf('.');
+ String packageName = lastDot == -1 ? "" : qualifiedName.substring(0, lastDot);
+ String thisPackage = Objects.toString(tree.getPackageName(), "");
+ // java.lang.* and current package are imported by default
+ if (!packageName.equals("java.lang")
+ && !packageName.equals(thisPackage)
+ && !packageName.equals("")) {
+ qualifiedNames.add(qualifiedName);
+ }
+ }
+ return null;
+ }
+ }
+ new FindUsedImports().scan(tree, null);
+ // Add qualified names from fixes
+ qualifiedNames.addAll(fixes.values());
+ return qualifiedNames;
+ }
}
diff --git a/src/test/java/org/javacs/JavaCompilerServiceTest.java b/src/test/java/org/javacs/JavaCompilerServiceTest.java
index 8320d0c..a067323 100644
--- a/src/test/java/org/javacs/JavaCompilerServiceTest.java
+++ b/src/test/java/org/javacs/JavaCompilerServiceTest.java
@@ -259,8 +259,8 @@ public class JavaCompilerServiceTest {
MethodInvocation found =
compiler.methodInvocation(URI.create("/Overloads.java"), contents("/Overloads.java"), 3, 15).get();
- assertThat(
- found.overloads, containsInAnyOrder(hasToString("print(int)"), hasToString("print(java.lang.String)")));
+ assertThat(found.overloads, hasItem(hasToString("print(int)")));
+ assertThat(found.overloads, hasToString("print(java.lang.String)"));
}
@Test
@@ -281,4 +281,11 @@ public class JavaCompilerServiceTest {
assertTrue(doc.isPresent());
assertThat(Javadocs.commentText(doc.get()).orElse("<empty>"), containsString("A great method"));
}
+
+ @Test
+ public void fixImports() {
+ Set<String> qualifiedNames =
+ compiler.fixImports(resourceUri("/MissingImport.java"), contents("/MissingImport.java"));
+ assertThat(qualifiedNames, hasItem("java.util.List"));
+ }
}
diff --git a/src/test/resources/MissingImport.java b/src/test/resources/MissingImport.java
new file mode 100644
index 0000000..79f0d92
--- /dev/null
+++ b/src/test/resources/MissingImport.java
@@ -0,0 +1,5 @@
+class MissingImport {
+ void test() {
+ List<String> x = null;
+ }
+} \ No newline at end of file