From a9f8a3aec7b861d865b1dc884e3b7dd17b1dd884 Mon Sep 17 00:00:00 2001 From: trett Date: Sun, 9 Jul 2023 11:18:00 +0200 Subject: [PATCH] Cleanup javac on remove/rename java class --- .../java/org/javacs/JavaCompilerService.java | 6 ++- .../java/org/javacs/JavaLanguageServer.java | 24 +++++++++++- .../java/org/javacs/ReusableCompiler.java | 22 ++++++++--- .../java/org/javacs/SourceFileManager.java | 12 ++++-- src/test/java/org/javacs/CompletionsTest.java | 37 +++++++++++++++++++ 5 files changed, 89 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/javacs/JavaCompilerService.java b/src/main/java/org/javacs/JavaCompilerService.java index cfe727830..71def8992 100644 --- a/src/main/java/org/javacs/JavaCompilerService.java +++ b/src/main/java/org/javacs/JavaCompilerService.java @@ -65,7 +65,7 @@ private void loadCompile(Collection sources) { cachedCompile.borrow.close(); } cachedCompile = doCompile(sources); - cachedModified.clear(); + clearCachedModified(); for (var f : sources) { cachedModified.put(f, f.getLastModified()); } @@ -350,5 +350,9 @@ public CompileTask compile(Collection sources) { return new CompileTask(compile.task, compile.roots, diags, compile::close); } + void clearCachedModified() { + cachedModified.clear(); + } + private static final Logger LOG = Logger.getLogger("main"); } diff --git a/src/main/java/org/javacs/JavaLanguageServer.java b/src/main/java/org/javacs/JavaLanguageServer.java index aa96ebd09..5d5393b48 100644 --- a/src/main/java/org/javacs/JavaLanguageServer.java +++ b/src/main/java/org/javacs/JavaLanguageServer.java @@ -4,6 +4,7 @@ import com.google.gson.*; import com.sun.source.util.Trees; +import com.sun.tools.javac.tree.JCTree; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; @@ -142,6 +143,7 @@ private Set docPath() { } return paths; } + private Set addExports() { if (!settings.has("addExports")) return Set.of(); var array = settings.getAsJsonArray("addExports"); @@ -241,10 +243,10 @@ public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) { FileStore.externalChange(file); break; case FileChangeType.Deleted: - FileStore.externalDelete(file); + removeClass(file); break; } - return; + continue; } var name = file.getFileName().toString(); switch (name) { @@ -480,6 +482,24 @@ private RenameVariable renameVariable(CompileTask task, VariableElement variable return new RenameVariable(file, (int) position, newName); } + private void removeClass(Path file) { + var className = cacheCompiler.fileManager.getClassName(file); + FileStore.externalDelete(file); + var compiler = compiler(); + var referencePaths = + Arrays.stream(compiler.findTypeReferences(className)).filter(ref -> !ref.equals(file)).toList(); + if (referencePaths.isEmpty()) { + return; + } + for (var referencePath : referencePaths) { + try (var task = compiler.compile(referencePath)) { + compiler.compiler.removeClass((JCTree.JCCompilationUnit) task.root(), className); + } + } + compiler.clearCachedModified(); + lint(referencePaths); + } + private boolean uncheckedChanges = false; private Path lastEdited = Paths.get(""); diff --git a/src/main/java/org/javacs/ReusableCompiler.java b/src/main/java/org/javacs/ReusableCompiler.java index 2d50f74c7..37669cdaa 100644 --- a/src/main/java/org/javacs/ReusableCompiler.java +++ b/src/main/java/org/javacs/ReusableCompiler.java @@ -31,14 +31,11 @@ import com.sun.source.util.TaskListener; import com.sun.tools.javac.api.*; import com.sun.tools.javac.code.Types; -import com.sun.tools.javac.comp.Annotate; -import com.sun.tools.javac.comp.Check; -import com.sun.tools.javac.comp.CompileStates; -import com.sun.tools.javac.comp.Enter; -import com.sun.tools.javac.comp.Modules; +import com.sun.tools.javac.comp.*; import com.sun.tools.javac.main.Arguments; import com.sun.tools.javac.main.JavaCompiler; import com.sun.tools.javac.model.JavacElements; +import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.DefinedBy; import com.sun.tools.javac.util.DefinedBy.Api; @@ -128,6 +125,10 @@ Borrow getTask( return new Borrow(task, currentContext); } + public void removeClass(JCTree.JCCompilationUnit root, String className) { + currentContext.removeClass(root, className); + } + class Borrow implements AutoCloseable { final JavacTask task; boolean closed; @@ -209,6 +210,10 @@ void drop(Class c) { ht.remove(key(c)); } + void removeClass(JCTree.JCCompilationUnit root, String className) { + ((ReusableJavaCompiler) get(JavaCompiler.compilerKey)).removeClass(root, className); + } + /** * Reusable JavaCompiler; exposes a method to clean up the component from leftovers associated with previous * compilations. @@ -226,6 +231,13 @@ public void close() { // do nothing } + void removeClass(JCTree.JCCompilationUnit root, String className) { + for (var classSymbol : syms.getClassesForName(names.fromString(className))) { + syms.removeClass(root.modle, classSymbol.flatname); + chk.removeCompiled(classSymbol); + } + } + void clear() { newRound(); } diff --git a/src/main/java/org/javacs/SourceFileManager.java b/src/main/java/org/javacs/SourceFileManager.java index 6d063c535..ec228d38b 100644 --- a/src/main/java/org/javacs/SourceFileManager.java +++ b/src/main/java/org/javacs/SourceFileManager.java @@ -42,15 +42,19 @@ private JavaFileObject asJavaFileObject(Path file) { public String inferBinaryName(Location location, JavaFileObject file) { if (location == StandardLocation.SOURCE_PATH) { var source = (SourceFileObject) file; - var packageName = FileStore.packageName(source.path); - var className = removeExtension(source.path.getFileName().toString()); - if (!packageName.isEmpty()) className = packageName + "." + className; - return className; + return getClassName(source.path); } else { return super.inferBinaryName(location, file); } } + String getClassName(Path path) { + var packageName = FileStore.packageName(path); + var className = removeExtension(path.getFileName().toString()); + if (!packageName.isEmpty()) className = packageName + "." + className; + return className; + } + private String removeExtension(String fileName) { var lastDot = fileName.lastIndexOf("."); return (lastDot == -1 ? fileName : fileName.substring(0, lastDot)); diff --git a/src/test/java/org/javacs/CompletionsTest.java b/src/test/java/org/javacs/CompletionsTest.java index bb7d9f5b7..ff0d9db5c 100644 --- a/src/test/java/org/javacs/CompletionsTest.java +++ b/src/test/java/org/javacs/CompletionsTest.java @@ -6,6 +6,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardOpenOption; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import org.javacs.completion.CompletionProvider; @@ -752,4 +753,40 @@ public void multilineChain() { var inserts = filterText("/org/javacs/example/MultilineChain.java", 6, 14); assertThat(inserts, hasItem("concat")); } + + @Test + public void removeCompiledClassShouldPublishCorrectDiagnostic() throws IOException { + var xClass = FindResource.path("/org/javacs/example/X.java"); + var yClass = FindResource.path("/org/javacs/example/Y.java"); + try { + try (var writer = Files.newBufferedWriter(xClass, StandardOpenOption.CREATE_NEW)) { + writer.write( + "package org.javacs.example;\n" + + "class X {\n " + + "static void test() {\n" + + "Y.test();\n" + + "}}"); + } + try (var writer = Files.newBufferedWriter(yClass, StandardOpenOption.CREATE_NEW)) { + writer.write( + "package org.javacs.example;\nclass Y {\n" + "static int test() {\n" + "return 1;\n" + "}}"); + } + List lintErrors = new ArrayList<>(); + var server = LanguageServerFixture.getJavaLanguageServer(diagnostic -> lintErrors.add(diagnostic.message)); + server.compiler().compile(xClass, yClass).close(); + + var deleteEvent = new FileEvent(); + deleteEvent.uri = yClass.toUri(); + deleteEvent.type = FileChangeType.Deleted; + var deleteFileParams = new DidChangeWatchedFilesParams(); + deleteFileParams.changes = List.of(deleteEvent); + server.didChangeWatchedFiles(deleteFileParams); + + assertEquals(1, lintErrors.size()); + assertTrue(lintErrors.stream().anyMatch(e -> e.contains("cannot find symbol"))); + } finally { + Files.delete(xClass); + Files.delete(yClass); + } + } }