Skip to content

Commit 073eca8

Browse files
trettgeorgewfraser
authored andcommitted
Cleanup javac on remove/rename java class
1 parent 843be94 commit 073eca8

5 files changed

Lines changed: 88 additions & 12 deletions

File tree

src/main/java/org/javacs/JavaCompilerService.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ private void loadCompile(Collection<? extends JavaFileObject> sources) {
6666
}
6767
cachedCompile = null;
6868
cachedCompile = doCompile(sources);
69-
cachedModified.clear();
69+
clearCachedModified();
7070
for (var f : sources) {
7171
cachedModified.put(f, f.getLastModified());
7272
}
@@ -359,5 +359,9 @@ public CompileTask compile(Collection<? extends JavaFileObject> sources) {
359359
return new CompileTask(compile.task, compile.roots, diags, compile::close);
360360
}
361361

362+
void clearCachedModified() {
363+
cachedModified.clear();
364+
}
365+
362366
private static final Logger LOG = Logger.getLogger("main");
363367
}

src/main/java/org/javacs/JavaLanguageServer.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import com.google.gson.*;
66
import com.sun.source.util.Trees;
7+
import com.sun.tools.javac.tree.JCTree;
78
import java.nio.file.Path;
89
import java.nio.file.Paths;
910
import java.time.Duration;
@@ -242,10 +243,10 @@ public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
242243
FileStore.externalChange(file);
243244
break;
244245
case FileChangeType.Deleted:
245-
FileStore.externalDelete(file);
246+
removeClass(file);
246247
break;
247248
}
248-
return;
249+
continue;
249250
}
250251
var name = file.getFileName().toString();
251252
switch (name) {
@@ -481,6 +482,24 @@ private RenameVariable renameVariable(CompileTask task, VariableElement variable
481482
return new RenameVariable(file, (int) position, newName);
482483
}
483484

485+
private void removeClass(Path file) {
486+
var className = cacheCompiler.fileManager.getClassName(file);
487+
FileStore.externalDelete(file);
488+
var compiler = compiler();
489+
var referencePaths =
490+
Arrays.stream(compiler.findTypeReferences(className)).filter(ref -> !ref.equals(file)).toList();
491+
if (referencePaths.isEmpty()) {
492+
return;
493+
}
494+
for (var referencePath : referencePaths) {
495+
try (var task = compiler.compile(referencePath)) {
496+
compiler.compiler.removeClass((JCTree.JCCompilationUnit) task.root(), className);
497+
}
498+
}
499+
compiler.clearCachedModified();
500+
lint(referencePaths);
501+
}
502+
484503
private boolean uncheckedChanges = false;
485504
private Path lastEdited = Paths.get("");
486505

src/main/java/org/javacs/ReusableCompiler.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,11 @@
3131
import com.sun.source.util.TaskListener;
3232
import com.sun.tools.javac.api.*;
3333
import com.sun.tools.javac.code.Types;
34-
import com.sun.tools.javac.comp.Annotate;
35-
import com.sun.tools.javac.comp.Check;
36-
import com.sun.tools.javac.comp.CompileStates;
37-
import com.sun.tools.javac.comp.Enter;
38-
import com.sun.tools.javac.comp.Modules;
34+
import com.sun.tools.javac.comp.*;
3935
import com.sun.tools.javac.main.Arguments;
4036
import com.sun.tools.javac.main.JavaCompiler;
4137
import com.sun.tools.javac.model.JavacElements;
38+
import com.sun.tools.javac.tree.JCTree;
4239
import com.sun.tools.javac.util.Context;
4340
import com.sun.tools.javac.util.DefinedBy;
4441
import com.sun.tools.javac.util.DefinedBy.Api;
@@ -128,6 +125,10 @@ Borrow getTask(
128125
return new Borrow(task, currentContext);
129126
}
130127

128+
public void removeClass(JCTree.JCCompilationUnit root, String className) {
129+
currentContext.removeClass(root, className);
130+
}
131+
131132
class Borrow implements AutoCloseable {
132133
final JavacTask task;
133134
boolean closed;
@@ -209,6 +210,10 @@ <T> void drop(Class<T> c) {
209210
ht.remove(key(c));
210211
}
211212

213+
void removeClass(JCTree.JCCompilationUnit root, String className) {
214+
((ReusableJavaCompiler) get(JavaCompiler.compilerKey)).removeClass(root, className);
215+
}
216+
212217
/**
213218
* Reusable JavaCompiler; exposes a method to clean up the component from leftovers associated with previous
214219
* compilations.
@@ -226,6 +231,13 @@ public void close() {
226231
// do nothing
227232
}
228233

234+
void removeClass(JCTree.JCCompilationUnit root, String className) {
235+
for (var classSymbol : syms.getClassesForName(names.fromString(className))) {
236+
syms.removeClass(root.modle, classSymbol.flatname);
237+
chk.removeCompiled(classSymbol);
238+
}
239+
}
240+
229241
void clear() {
230242
newRound();
231243
}

src/main/java/org/javacs/SourceFileManager.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,19 @@ private JavaFileObject asJavaFileObject(Path file) {
4242
public String inferBinaryName(Location location, JavaFileObject file) {
4343
if (location == StandardLocation.SOURCE_PATH) {
4444
var source = (SourceFileObject) file;
45-
var packageName = FileStore.packageName(source.path);
46-
var className = removeExtension(source.path.getFileName().toString());
47-
if (packageName != null && !packageName.isEmpty()) className = packageName + "." + className;
48-
return className;
45+
return getClassName(source.path);
4946
} else {
5047
return super.inferBinaryName(location, file);
5148
}
5249
}
5350

51+
String getClassName(Path path) {
52+
var packageName = FileStore.packageName(path);
53+
var className = removeExtension(path.getFileName().toString());
54+
if (packageName != null && !packageName.isEmpty()) className = packageName + "." + className;
55+
return className;
56+
}
57+
5458
private String removeExtension(String fileName) {
5559
var lastDot = fileName.lastIndexOf(".");
5660
return (lastDot == -1 ? fileName : fileName.substring(0, lastDot));

src/test/java/org/javacs/CompletionsTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.io.IOException;
88
import java.nio.file.Files;
99
import java.nio.file.StandardOpenOption;
10+
import java.util.ArrayList;
1011
import java.util.List;
1112
import java.util.stream.Collectors;
1213
import org.javacs.completion.CompletionProvider;
@@ -753,4 +754,40 @@ public void multilineChain() {
753754
var inserts = filterText("/org/javacs/example/MultilineChain.java", 6, 14);
754755
assertThat(inserts, hasItem("concat"));
755756
}
757+
758+
@Test
759+
public void removeCompiledClassShouldPublishCorrectDiagnostic() throws IOException {
760+
var xClass = FindResource.path("/org/javacs/example/X.java");
761+
var yClass = FindResource.path("/org/javacs/example/Y.java");
762+
try {
763+
try (var writer = Files.newBufferedWriter(xClass, StandardOpenOption.CREATE_NEW)) {
764+
writer.write(
765+
"package org.javacs.example;\n"
766+
+ "class X {\n "
767+
+ "static void test() {\n"
768+
+ "Y.test();\n"
769+
+ "}}");
770+
}
771+
try (var writer = Files.newBufferedWriter(yClass, StandardOpenOption.CREATE_NEW)) {
772+
writer.write(
773+
"package org.javacs.example;\nclass Y {\n" + "static int test() {\n" + "return 1;\n" + "}}");
774+
}
775+
List<String> lintErrors = new ArrayList<>();
776+
var server = LanguageServerFixture.getJavaLanguageServer(diagnostic -> lintErrors.add(diagnostic.message));
777+
server.compiler().compile(xClass, yClass).close();
778+
779+
var deleteEvent = new FileEvent();
780+
deleteEvent.uri = yClass.toUri();
781+
deleteEvent.type = FileChangeType.Deleted;
782+
var deleteFileParams = new DidChangeWatchedFilesParams();
783+
deleteFileParams.changes = List.of(deleteEvent);
784+
server.didChangeWatchedFiles(deleteFileParams);
785+
786+
assertEquals(1, lintErrors.size());
787+
assertTrue(lintErrors.stream().anyMatch(e -> e.contains("cannot find symbol")));
788+
} finally {
789+
Files.delete(xClass);
790+
Files.delete(yClass);
791+
}
792+
}
756793
}

0 commit comments

Comments
 (0)