diff options
-rw-r--r-- | src/main/java/org/javacs/FixImports.java | 6 | ||||
-rw-r--r-- | src/main/java/org/javacs/JavaCompilerService.java | 59 | ||||
-rw-r--r-- | src/test/java/org/javacs/JavaCompilerServiceTest.java | 11 | ||||
-rw-r--r-- | src/test/resources/MissingImport.java | 5 |
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 |