diff options
author | George Fraser <george@fivetran.com> | 2017-03-19 13:38:40 -0700 |
---|---|---|
committer | George Fraser <george@fivetran.com> | 2017-03-19 13:38:40 -0700 |
commit | 69328dca64e15d6acea74866555de7feb462db5b (patch) | |
tree | f4e882432695eacdeb0b32545575c56ad6e6d84e /src | |
parent | 139784693f4bef6078fdc58e7ce17a9416e1a6c8 (diff) | |
download | java-language-server-69328dca64e15d6acea74866555de7feb462db5b.zip |
Fixed most tests
Diffstat (limited to 'src')
22 files changed, 593 insertions, 425 deletions
diff --git a/src/main/java/org/javacs/CompilationResult.java b/src/main/java/org/javacs/CompilationResult.java new file mode 100644 index 0000000..a3f0930 --- /dev/null +++ b/src/main/java/org/javacs/CompilationResult.java @@ -0,0 +1,17 @@ +package org.javacs; + +import com.sun.tools.javac.tree.JCTree; + +import javax.tools.DiagnosticCollector; +import javax.tools.JavaFileObject; +import java.util.Collection; + +class CompilationResult { + final Collection<JCTree.JCCompilationUnit> trees; + final DiagnosticCollector<JavaFileObject> errors; + + CompilationResult(Collection<JCTree.JCCompilationUnit> trees, DiagnosticCollector<JavaFileObject> errors) { + this.trees = trees; + this.errors = errors; + } +} diff --git a/src/main/java/org/javacs/GetResourceFileObject.java b/src/main/java/org/javacs/GetResourceFileObject.java deleted file mode 100644 index 79ed08a..0000000 --- a/src/main/java/org/javacs/GetResourceFileObject.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.javacs; - -import com.google.common.io.CharStreams; - -import javax.tools.SimpleJavaFileObject; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URI; -import java.net.URISyntaxException; - -/** - * Represents a java source on the system resource path. - */ -public class GetResourceFileObject extends SimpleJavaFileObject { - public final String path; - - public GetResourceFileObject(String path) { - super(getResourceUri(path), Kind.SOURCE); - - this.path = path; - } - - private static URI getResourceUri(String path) { - try { - return GetResourceFileObject.class.getResource(path).toURI(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - try (Reader r = new InputStreamReader(openInputStream())) { - return CharStreams.toString(r); - } - } - - @Override - public InputStream openInputStream() throws IOException { - return GetResourceFileObject.class.getResourceAsStream(path); - } -} diff --git a/src/main/java/org/javacs/JavaLanguageServer.java b/src/main/java/org/javacs/JavaLanguageServer.java index f22156d..4611b96 100644 --- a/src/main/java/org/javacs/JavaLanguageServer.java +++ b/src/main/java/org/javacs/JavaLanguageServer.java @@ -9,7 +9,6 @@ import org.eclipse.lsp4j.services.WorkspaceService; import org.w3c.dom.Document; import org.xml.sax.SAXException; -import javax.tools.DiagnosticCollector; import javax.tools.JavaFileObject; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -283,7 +282,9 @@ class JavaLanguageServer implements LanguageServer { } files.forEach((config, configFiles) -> { - publishDiagnostics(paths, findCompilerForConfig(config).update(configFiles)); + CompilationResult compile = findCompilerForConfig(config).compile(configFiles); + + publishDiagnostics(compile); }); } @@ -324,7 +325,9 @@ class JavaLanguageServer implements LanguageServer { activeDocuments.remove(uri); findCompiler(uri).ifPresent(compiler -> { - compiler.delete(uri); + CompilationResult result = compiler.delete(uri); + + publishDiagnostics(result); }); } } @@ -336,12 +339,16 @@ class JavaLanguageServer implements LanguageServer { }; } - private void publishDiagnostics(Collection<URI> paths, DiagnosticCollector<JavaFileObject> errors) { + private void publishDiagnostics(CompilationResult result) { + List<URI> touched = result.trees + .stream() + .map(tree -> tree.getSourceFile().toUri()) + .collect(Collectors.toList()); Map<URI, PublishDiagnosticsParams> files = new HashMap<>(); + + touched.forEach(p -> files.put(p, newPublishDiagnostics(p))); - paths.forEach(p -> files.put(p, newPublishDiagnostics(p))); - - errors.getDiagnostics().forEach(error -> { + result.errors.getDiagnostics().forEach(error -> { if (error.getStartPosition() != javax.tools.Diagnostic.NOPOS) { URI uri = error.getSource().toUri(); PublishDiagnosticsParams publish = files.computeIfAbsent(uri, this::newPublishDiagnostics); @@ -670,6 +677,13 @@ class JavaLanguageServer implements LanguageServer { .orElse(Collections.emptyList()); } + public Optional<Symbol> findSymbol(URI file, int line, int character) { + Optional<String> content = activeContent(file); + long cursor = findOffset(file, line, character); + + return findCompiler(file).flatMap(compiler -> compiler.symbolAt(file, content, cursor)); + } + private List<? extends SymbolInformation> findDocumentSymbols(DocumentSymbolParams params) { URI uri = URI.create(params.getTextDocument().getUri()); diff --git a/src/main/java/org/javacs/JavacHolder.java b/src/main/java/org/javacs/JavacHolder.java index d0ac625..0fd9ada 100644 --- a/src/main/java/org/javacs/JavacHolder.java +++ b/src/main/java/org/javacs/JavacHolder.java @@ -106,7 +106,7 @@ public class JavacHolder { log.multipleErrors = true; } - public final JavacFileManager fileManager = new JavacFileManager(context, true, null); + private final JavacFileManager fileManager = new JavacFileManager(context, true, null); private final ForgivingAttr attr = ForgivingAttr.instance(context); private final Check check = Check.instance(context); private final FuzzyParserFactory parserFactory = FuzzyParserFactory.instance(context); @@ -129,7 +129,7 @@ public class JavacHolder { // Set up SymbolIndex /** - * Index of symbols that gets updated every time you call update + * Index of symbols that gets updated every time you call compile */ public final SymbolIndex index = new SymbolIndex(this); @@ -139,6 +139,10 @@ public class JavacHolder { public final CompletableFuture<Void> initialIndexComplete; public JavacHolder(Set<Path> classPath, Set<Path> sourcePath, Path outputDirectory) { + this(classPath, sourcePath, outputDirectory, true); + } + + public JavacHolder(Set<Path> classPath, Set<Path> sourcePath, Path outputDirectory, boolean index) { this.classPath = Collections.unmodifiableSet(classPath); this.sourcePath = Collections.unmodifiableSet(sourcePath); this.outputDirectory = outputDirectory; @@ -151,7 +155,10 @@ public class JavacHolder { ensureOutputDirectory(outputDirectory); clearOutputDirectory(outputDirectory); - initialIndexComplete = startIndexingSourcePath(); + if (index) + initialIndexComplete = startIndexingSourcePath(); + else + initialIndexComplete = CompletableFuture.completedFuture(null); } private void logStartStopEvents() { @@ -179,22 +186,22 @@ public class JavacHolder { private CompletableFuture<Void> startIndexingSourcePath() { CompletableFuture<Void> done = new CompletableFuture<>(); Thread worker = new Thread("InitialIndex") { - List<JCTree.JCCompilationUnit> parsed = new ArrayList<>(); - List<Path> paths = new ArrayList<>(); - @Override public void run() { + List<URI> objects = new ArrayList<>(); + // Parse each file - sourcePath.forEach(s -> parseAll(s, parsed, paths)); + sourcePath.forEach(s -> findAllFiles(s, objects)); // Compile all parsed files - compile(parsed); + Map<URI, Optional<String>> files = objects.stream().collect(Collectors.toMap(key -> key, key -> Optional.empty())); + CompilationResult result = doCompile(files); - parsed.forEach(index::update); + result.trees.forEach(index::update); // TODO minimize memory use during this process - // Instead of doing parse-all / compile-all, - // queue all files, then do parse / compile on each + // Instead of doing parse-all / compileFileObjects-all, + // queue all files, then do parse / compileFileObjects on each // If invoked correctly, javac should avoid reparsing the same file twice // Then, use the same mechanism as the desugar / generate phases to remove method bodies, // to reclaim memory as we go @@ -207,19 +214,16 @@ public class JavacHolder { /** * Look for .java files and invalidate them */ - private void parseAll(Path path, List<JCTree.JCCompilationUnit> trees, List<Path> paths) { + private void findAllFiles(Path path, List<URI> uris) { if (Files.isDirectory(path)) try { - Files.list(path).forEach(p -> parseAll(p, trees, paths)); + Files.list(path).forEach(p -> findAllFiles(p, uris)); } catch (IOException e) { throw new UncheckedIOException(e); } else if (path.getFileName().toString().endsWith(".java")) { LOG.info("Index " + path); - JavaFileObject file = fileManager.getRegularFile(path.toFile()); - - trees.add(parse(file)); - paths.add(path); + uris.add(path.toUri()); } } }; @@ -269,19 +273,18 @@ public class JavacHolder { * @param cursor Offset in file where the cursor is */ public List<CompletionItem> autocomplete(URI file, Optional<String> textContent, long cursor) { + initialIndexComplete.join(); + JavaFileObject object = findFile(file, textContent); object = TreePruner.putSemicolonAfterCursor(object, file, cursor); - JCTree.JCCompilationUnit tree = parse(object); - - // Remove all statements after the cursor - // There are often parse errors after the cursor, which can generate unrecoverable type errors - new TreePruner(tree, context).removeStatementsAfterCursor(cursor); - - compile(Collections.singleton(tree)); + JCTree.JCCompilationUnit compiled = compileSimple( + object, + tree -> new TreePruner(tree, context).removeStatementsAfterCursor(cursor) + ); - return doAutocomplete(object, tree, cursor); + return doAutocomplete(object, compiled, cursor); } private List<CompletionItem> doAutocomplete(JavaFileObject object, JCTree.JCCompilationUnit pruned, long cursor) { @@ -300,6 +303,8 @@ public class JavacHolder { * @param cursor Offset in file where the cursor is */ public List<Location> findReferences(URI file, Optional<String> textContent, long cursor) { + initialIndexComplete.join(); + JCTree.JCCompilationUnit tree = findTree(file, textContent); return findSymbol(tree, cursor) @@ -316,6 +321,8 @@ public class JavacHolder { } public Optional<Location> gotoDefinition(URI file, Optional<String> textContent, long cursor) { + initialIndexComplete.join(); + JCTree.JCCompilationUnit tree = findTree(file, textContent); return findSymbol(tree, cursor) @@ -334,13 +341,7 @@ public class JavacHolder { } private JCTree.JCCompilationUnit findTree(URI file, Optional<String> textContent) { - JCTree.JCCompilationUnit tree = parse(findFile(file, textContent)); - - compile(Collections.singleton(tree)); - - index.update(tree, context); - - return tree; + return compileSimple(findFile(file, textContent), parsed -> {}); } private Optional<Symbol> findSymbol(JCTree.JCCompilationUnit tree, long cursor) { @@ -353,48 +354,27 @@ public class JavacHolder { } public Optional<Symbol> symbolAt(URI file, Optional<String> textContent, long cursor) { + initialIndexComplete.join(); + JCTree.JCCompilationUnit tree = findTree(file, textContent); return findSymbol(tree, cursor); } /** - * Clear files and all their dependents, recompile, update the index, and report any errors. + * File has been deleted */ - public DiagnosticCollector<JavaFileObject> update(Map<URI, Optional<String>> files) { - List<JavaFileObject> objects = files - .entrySet() - .stream() - .map(e -> findFile(e.getKey(), e.getValue())) - .collect(Collectors.toList()); - - return doUpdate(objects); - } + public CompilationResult delete(URI uri) { + initialIndexComplete.join(); - /** - * Exposed for testing only! - */ - public DiagnosticCollector<JavaFileObject> doUpdate(Collection<JavaFileObject> objects) { - List<JCTree.JCCompilationUnit> parsed = objects + JavaFileObject object = findFile(uri, Optional.empty()); + Map<URI, Optional<String>> deps = dependencies(Collections.singleton(object)) .stream() - .map(f -> { - clear(f); + .collect(Collectors.toMap(o -> o.toUri(), o -> Optional.empty())); - return f; - }) - .map(this::parse) - .collect(Collectors.toList()); - - // TODO add all dependents + clear(object); - return compile(parsed); - } - - /** - * File has been deleted - */ - public void delete(URI uri) { - // TODO + return compile(deps); } private JavaFileObject findFile(URI file, Optional<String> text) { @@ -403,58 +383,113 @@ public class JavacHolder { .orElse(fileManager.getRegularFile(new File(file))); } - /** - * Parse the indicated source file, and its dependencies if they have been modified. - */ - public JCTree.JCCompilationUnit parse(JavaFileObject source) { - clear(source); + private DiagnosticCollector<JavaFileObject> startCollectingErrors() { + DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - JCTree.JCCompilationUnit result = compiler.parse(source); + onErrorDelegate = error -> { + if (error.getStartPosition() != Diagnostic.NOPOS) + errors.report(error); + else + LOG.warning("Skipped " + error.getMessage(null)); + }; + return errors; + } - return result; + private void stopCollectingErrors() { + onErrorDelegate = error -> {}; } /** - * Compile a set of parsed files. - * - * If these files reference un-parsed dependencies, those dependencies will also be parsed and compiled. + * Clear files and all their dependents, recompile, compile the index, and report any errors. + * + * If these files reference un-compiled dependencies, those dependencies will also be parsed and compiled. */ - public DiagnosticCollector<JavaFileObject> compile(Collection<JCTree.JCCompilationUnit> parsed) { - try { - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); + public CompilationResult compile(Map<URI, Optional<String>> files) { + initialIndexComplete.join(); - onErrorDelegate = error -> { - if (error.getStartPosition() != Diagnostic.NOPOS) - errors.report(error); - }; + return doCompile(files); + } + + private CompilationResult doCompile(Map<URI, Optional<String>> files) { + List<JavaFileObject> objects = files + .entrySet() + .stream() + .map(e -> findFile(e.getKey(), e.getValue())) + .collect(Collectors.toList()); - compiler.processAnnotations(compiler.enterTrees(com.sun.tools.javac.util.List.from(parsed))); + objects.addAll(dependencies(objects)); - while (!todo.isEmpty()) { - Env<AttrContext> next = todo.remove(); + // Clear javac caches + objects.forEach(this::clear); - try { - // We don't do the desugar or generate phases, because they remove method bodies and methods - Env<AttrContext> attributedTree = compiler.attribute(next); - Queue<Env<AttrContext>> analyzedTree = compiler.flow(attributedTree); - } catch (Exception e) { - LOG.log(Level.SEVERE, "Error compiling " + next.toplevel.sourcefile.getName(), e); + try { + DiagnosticCollector<JavaFileObject> errors = startCollectingErrors(); - // Keep going - } - } + List<JCTree.JCCompilationUnit> parsed = objects.stream() + .map(compiler::parse) + .collect(Collectors.toList()); - return errors; + compileTrees(parsed); + + parsed.forEach(index::update); + + return new CompilationResult(parsed, errors); } finally { - onErrorDelegate = error -> {}; + stopCollectingErrors(); } } + private void compileTrees(Collection<JCTree.JCCompilationUnit> parsed) { + compiler.processAnnotations(compiler.enterTrees(com.sun.tools.javac.util.List.from(parsed))); + + while (!todo.isEmpty()) { + Env<AttrContext> next = todo.remove(); + + try { + // We don't do the desugar or generate phases, because they remove method bodies and methods + Env<AttrContext> attributedTree = compiler.attribute(next); + Queue<Env<AttrContext>> analyzedTree = compiler.flow(attributedTree); + } catch (Exception e) { + LOG.log(Level.SEVERE, "Error compiling " + next.toplevel.sourcefile.getName(), e); + + // Keep going + } + } + } + + /** + * Compile without updating dependencies, index, collecting errors. + * Useful for operations like autocomplete. + */ + private JCTree.JCCompilationUnit compileSimple(JavaFileObject read, Consumer<JCTree.JCCompilationUnit> afterParse) { + clear(read); + + JCTree.JCCompilationUnit parse = compiler.parse(read); + + afterParse.accept(parse); + compileTrees(Collections.singleton(parse)); + + return parse; + } + + private Collection<JavaFileObject> dependencies(Collection<JavaFileObject> files) { + // TODO + return Collections.emptyList(); + } + + /** + * Clear file from javac's caches. + * This is automatically invoked by other methods of this class; it's exposed only for testing. + */ + public void clear(URI file) { + clear(findFile(file, Optional.empty())); + } + /** * Clear a file from javac's internal caches */ private void clear(JavaFileObject source) { - // TODO clear dependencies as well (dependencies should get stored in SymbolIndex) + index.clear(source.toUri()); // Forget about this file Consumer<JavaFileObject> removeFromLog = logRemover(log); @@ -471,14 +506,13 @@ public class JavacHolder { Consumer<Type> removeFromClosureCache = closureCacheRemover(types); check.compiled.forEach((name, symbol) -> { - if (symbol.sourcefile.getName().equals(source.getName())) + if (symbol.sourcefile.getName().equals(source.getName())) { + removeFromClosureCache.accept(symbol.type); remove.add(name); - - removeFromClosureCache.accept(symbol.type); + } }); remove.forEach(check.compiled::remove); - } /** diff --git a/src/main/java/org/javacs/SymbolIndex.java b/src/main/java/org/javacs/SymbolIndex.java index d2fd1c1..40d79ae 100644 --- a/src/main/java/org/javacs/SymbolIndex.java +++ b/src/main/java/org/javacs/SymbolIndex.java @@ -443,15 +443,6 @@ public class SymbolIndex { } /** - * Update the index when a files changes - */ - public void update(JCTree.JCCompilationUnit tree, Context context) { - Indexer indexer = new Indexer(context); - - tree.accept(indexer); - } - - /** * Clear a file from the index when it is deleted */ public void clear(URI sourceFile) { diff --git a/src/test/java/org/javacs/AutocompleteTest.java b/src/test/java/org/javacs/AutocompleteTest.java index 1f0a3eb..4640f25 100644 --- a/src/test/java/org/javacs/AutocompleteTest.java +++ b/src/test/java/org/javacs/AutocompleteTest.java @@ -1,12 +1,13 @@ package org.javacs; -import org.eclipse.lsp4j.*; +import org.eclipse.lsp4j.CompletionItem; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.eclipse.lsp4j.TextDocumentPositionParams; import org.junit.Ignore; import org.junit.Test; import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; import java.util.List; import java.util.Set; import java.util.logging.Logger; @@ -16,7 +17,7 @@ import java.util.stream.Stream; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; -public class AutocompleteTest extends Fixtures { +public class AutocompleteTest { private static final Logger LOG = Logger.getLogger("main"); @Test @@ -417,6 +418,8 @@ public class AutocompleteTest extends Fixtures { .collect(Collectors.toSet()); } + private static final JavaLanguageServer server = LanguageServerFixture.getJavaLanguageServer(); + private List<? extends CompletionItem> items(String file, int row, int column) { TextDocumentPositionParams position = new TextDocumentPositionParams(); @@ -424,18 +427,8 @@ public class AutocompleteTest extends Fixtures { position.getPosition().setLine(row); position.getPosition().setCharacter(column); position.setTextDocument(new TextDocumentIdentifier()); - position.getTextDocument().setUri(uri(file).toString()); - - JavaLanguageServer server = getJavaLanguageServer(); + position.getTextDocument().setUri(FindResource.uri(file).toString()); return server.autocomplete(position).getItems(); } - - private URI uri(String file) { - try { - return AutocompleteTest.class.getResource(file).toURI(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } } diff --git a/src/test/java/org/javacs/CompilerProfiling.java b/src/test/java/org/javacs/CompilerProfiling.java index 5056a88..dc2edc4 100644 --- a/src/test/java/org/javacs/CompilerProfiling.java +++ b/src/test/java/org/javacs/CompilerProfiling.java @@ -6,26 +6,24 @@ import com.sun.tools.javac.util.Context; import org.junit.Ignore; import org.junit.Test; -import javax.tools.DiagnosticCollector; -import javax.tools.JavaFileObject; import java.io.IOException; +import java.net.URI; import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.Collections; +import java.util.Optional; import java.util.logging.Logger; import static org.junit.Assert.assertNotNull; @Ignore -public class CompilerProfiling extends Fixtures { +public class CompilerProfiling { private static final Logger LOG = Logger.getLogger("main"); @Test public void parsingSpeed() throws IOException, URISyntaxException { - StringFileObject file = fromResource("/org/javacs/example/LargeFile.java"); + URI file = FindResource.uri("/org/javacs/example/LargeFile.java"); for (int i = 0; i < 10; i++) { Duration duration = compileLargeFile(file); @@ -34,19 +32,14 @@ public class CompilerProfiling extends Fixtures { } } - private Duration compileLargeFile(StringFileObject file) { + private Duration compileLargeFile(URI file) { long start = System.nanoTime(); - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - JavacHolder compiler = new JavacHolder(Collections.emptySet(), Collections.emptySet(), Paths.get("out")); + JavacHolder compiler = new JavacHolder(Collections.emptySet(), Collections.emptySet(), Paths.get("out"), false); GetCompilationUnit compilationUnit = new GetCompilationUnit(compiler.context); - compiler.onError(errors); - try { - JCTree.JCCompilationUnit tree = compiler.parse(file); - - tree.accept(compilationUnit); + compiler.compile(Collections.singletonMap(file, Optional.empty())).trees.forEach(tree -> tree.accept(compilationUnit)); } catch (RuntimeException e) { if (e.getCause() instanceof AbortCompilation) LOG.info("Aborted further stages"); @@ -61,12 +54,6 @@ public class CompilerProfiling extends Fixtures { return Duration.ofNanos(finish - start); } - private StringFileObject fromResource(String file) throws URISyntaxException, IOException { - Path path = Paths.get(LinterTest.class.getResource(file).toURI()); - String content = new String(Files.readAllBytes(path)); - return new StringFileObject(content, path); - } - private static class GetCompilationUnit extends BaseScanner { private CompilationUnitTree result; diff --git a/src/test/java/org/javacs/ContextPrinter.java b/src/test/java/org/javacs/ContextPrinter.java new file mode 100644 index 0000000..c61827c --- /dev/null +++ b/src/test/java/org/javacs/ContextPrinter.java @@ -0,0 +1,218 @@ +package org.javacs; + +import com.sun.tools.javac.util.Context; + +import java.lang.reflect.Field; +import java.util.*; + +public class ContextPrinter { + public static Tree tree(Context context, int depth) throws NoSuchFieldException, IllegalAccessException { + Field ht = Context.class.getDeclaredField("ht"); + + ht.setAccessible(true); + + Object value = ht.get(context); + TreeConverter treeConverter = new TreeConverter(); + + treeConverter.seen.add(context); + + return treeConverter.convert(value, depth); + } +} + +class TreeConverter { + Set<Object> seen = new HashSet<>(); + + Tree convert(Object o, int depth) throws IllegalAccessException { + if (o == null) + return new Leaf("null"); + else if (seen.contains(o)) + return new Leaf("<" + o.getClass().getSimpleName() + ">"); + else { + seen.add(o); + + if (o instanceof Map) { + Node node = Node.anonymous(); + Map map = (Map) o; + + for (Object key : map.keySet()) { + Object value = map.get(key); + + node.children.put(keyString(key, value, node.children.keySet()), convert(value, depth)); + } + + return node; + } else if (o instanceof Iterable) { + Seq seq = new Seq(); + Iterable it = (Iterable) o; + + for (Object each : it) + seq.children.add(convert(each, depth)); + + return seq; + } else if (depth > 0) { + Class<?> klass = o.getClass(); + Node node = Node.named(klass.getSimpleName()); + + for (Field field : klass.getDeclaredFields()) { + field.setAccessible(true); + + Object value = field.get(o); + + node.children.put(field.getName(), convert(value, depth - 1)); + } + + return node; + } + else return new Leaf("<" + o.getClass().getSimpleName() + ">"); + } + } + + private String keyString(Object key, Object value, Set<String> used) { + if (key instanceof Context.Key) + return "Context.Key<" + value.getClass().getSimpleName() + ">"; + else + return key.toString(); + } +} + +class IndentPrinter { + private final StringBuilder out = new StringBuilder(); + private int indent = 0; + private boolean startOfLine = true; + + IndentPrinter increaseIndent() { + indent++; + + return this; + } + + IndentPrinter decreaseIndent() { + indent--; + + return this; + } + + IndentPrinter append(String content) { + if (startOfLine) { + for (int i = 0; i < indent; i++) + out.append(" "); + + startOfLine = false; + } + + out.append(content); + + return this; + } + + IndentPrinter newline() { + out.append("\n"); + startOfLine = true; + + return this; + } + + @Override + public String toString() { + return out.toString(); + } +} + +abstract class Tree { + protected abstract void print(IndentPrinter out); + + @Override + public String toString() { + IndentPrinter printer = new IndentPrinter(); + + print(printer); + + return printer.toString(); + } +} + +class Node extends Tree { + final Optional<String> name; + final Map<String, Tree> children = new HashMap<>(); + + private Node(Optional<String> name) { + this.name = name; + } + + static Node anonymous() { + return new Node(Optional.empty()); + } + + static Node named(String name) { + return new Node(Optional.of(name)); + } + + @Override + public void print(IndentPrinter out) { + name.ifPresent(out::append); + + out.append("{").newline().increaseIndent(); + + children.forEach((key, value) -> { + out.append(key).append(": "); + value.print(out); + out.newline(); + }); + + out.decreaseIndent().append("}"); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Node node = (Node) o; + return Objects.equals(name, node.name) && + Objects.equals(children, node.children); + } + + @Override + public int hashCode() { + return Objects.hash(name, children); + } +} + +class Seq extends Tree { + final List<Tree> children = new ArrayList<>(); + + @Override + public void print(IndentPrinter out) { + out.append("[").newline().increaseIndent(); + + children.forEach(child -> child.print(out)); + + out.decreaseIndent().append("]"); + } +} + +class Leaf extends Tree { + final String value; + + Leaf(String value) { + this.value = value; + } + + @Override + public void print(IndentPrinter out) { + out.append(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Leaf leaf = (Leaf) o; + return Objects.equals(value, leaf.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } +}
\ No newline at end of file diff --git a/src/test/java/org/javacs/FindResource.java b/src/test/java/org/javacs/FindResource.java new file mode 100644 index 0000000..355223a --- /dev/null +++ b/src/test/java/org/javacs/FindResource.java @@ -0,0 +1,24 @@ +package org.javacs; + +import java.io.File; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Represents a java source on the system resource path. + */ +public class FindResource { + public static URI uri(String resourcePath) { + if (resourcePath.startsWith("/")) + resourcePath = resourcePath.substring(1); + + Path path = Paths.get("./src/test/resources").resolve(resourcePath).normalize(); + File file = path.toFile(); + + if (!file.exists()) + throw new RuntimeException(file + " does not exist"); + + return file.toURI(); + } +} diff --git a/src/test/java/org/javacs/GotoTest.java b/src/test/java/org/javacs/GotoTest.java index afda170..66f3820 100644 --- a/src/test/java/org/javacs/GotoTest.java +++ b/src/test/java/org/javacs/GotoTest.java @@ -6,20 +6,16 @@ import org.junit.Test; import java.io.IOException; import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Paths; import java.util.List; import java.util.logging.Logger; import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.hasItems; -import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; -public class GotoTest extends Fixtures { +public class GotoTest { private static final Logger LOG = Logger.getLogger("main"); private static final String file = "/org/javacs/example/Goto.java"; - private static final URI uri = uri(file); + private static final URI uri = FindResource.uri(file); @Test public void localVariable() throws IOException { @@ -68,6 +64,8 @@ public class GotoTest extends Fixtures { public void staticMethod() throws IOException { List<? extends Location> suggestions = doGoto(file, 15, 13); + System.out.println(suggestions.size()); + assertThat(suggestions, contains(location(uri, 37, 25, 37, 37))); } @@ -124,10 +122,12 @@ public class GotoTest extends Fixtures { return location; } + private static final JavaLanguageServer server = LanguageServerFixture.getJavaLanguageServer(); + private List<? extends Location> doGoto(String file, int row, int column) throws IOException { TextDocumentIdentifier document = new TextDocumentIdentifier(); - document.setUri(uri(file).toString()); + document.setUri(FindResource.uri(file).toString()); Position position = new Position(); @@ -139,16 +139,7 @@ public class GotoTest extends Fixtures { p.setTextDocument(document); p.setPosition(position); - JavaLanguageServer server = Fixtures.getJavaLanguageServer(); - return server.gotoDefinition(p); } - private static URI uri(String file) { - try { - return GotoTest.class.getResource(file).toURI(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } } diff --git a/src/test/java/org/javacs/JavaCompilerTest.java b/src/test/java/org/javacs/JavaCompilerTest.java index 4c90cf2..b0dc956 100644 --- a/src/test/java/org/javacs/JavaCompilerTest.java +++ b/src/test/java/org/javacs/JavaCompilerTest.java @@ -4,18 +4,21 @@ import com.google.common.collect.ImmutableList; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.util.JavacTask; import com.sun.tools.javac.api.JavacTool; -import com.sun.tools.javac.tree.JCTree; import org.junit.Ignore; import org.junit.Test; import javax.lang.model.element.Element; -import javax.tools.*; +import javax.tools.Diagnostic; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Paths; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.logging.Logger; // TODO java compiler can fail badly, handle somehow @@ -41,13 +44,9 @@ public class JavaCompilerTest { @Test public void javacHolder() { - JavacHolder javac = new JavacHolder(Collections.emptySet(), Collections.singleton(Paths.get("src/test/resources")), Paths.get("target")); + JavacHolder javac = new JavacHolder(Collections.emptySet(), Collections.singleton(Paths.get("src/test/resources")), Paths.get("target"), false); File file = Paths.get("src/test/resources/org/javacs/example/Bad.java").toFile(); - JCTree.JCCompilationUnit parsed = javac.parse(javac.fileManager.getRegularFile(file)); - - javac.compile(Collections.singleton(parsed)); - - LOG.info(parsed.toString()); + CompilationResult compile = javac.compile(Collections.singletonMap(file.toURI(), Optional.empty())); } private void reportError(Diagnostic<? extends JavaFileObject> error) { diff --git a/src/test/java/org/javacs/JavacHolderTest.java b/src/test/java/org/javacs/JavacHolderTest.java new file mode 100644 index 0000000..2f6f5e7 --- /dev/null +++ b/src/test/java/org/javacs/JavacHolderTest.java @@ -0,0 +1,30 @@ +package org.javacs; + +import org.junit.Test; + +import java.net.URI; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.Optional; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; + +public class JavacHolderTest { + @Test + public void clear() throws NoSuchFieldException, IllegalAccessException { + JavacHolder javac = new JavacHolder(Collections.emptySet(), Collections.singleton(Paths.get("src/test/resources")), Paths.get("target"), false); + URI file = FindResource.uri("org/javacs/example/FooString.java"); + CompilationResult compile = javac.compile(Collections.singletonMap(file, Optional.empty())); + Tree tree = ContextPrinter.tree(javac.context, 3); + + assertThat(tree.toString(), containsString("FooString")); + + javac.clear(file); + + System.out.println(tree.toString()); + + assertThat(tree.toString(), not(containsString("FooString"))); + } +} diff --git a/src/test/java/org/javacs/Fixtures.java b/src/test/java/org/javacs/LanguageServerFixture.java index f3c6db6..3c24acb 100644 --- a/src/test/java/org/javacs/Fixtures.java +++ b/src/test/java/org/javacs/LanguageServerFixture.java @@ -1,23 +1,19 @@ package org.javacs; import org.eclipse.lsp4j.InitializeParams; -import org.junit.BeforeClass; -import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; import java.util.Set; -public class Fixtures { +public class LanguageServerFixture { - public static void init() { } - - static JavaLanguageServer getJavaLanguageServer() { + public static JavaLanguageServer getJavaLanguageServer() { Set<Path> classPath = Collections.emptySet(); Set<Path> sourcePath = Collections.singleton(Paths.get("src/test/resources").toAbsolutePath()); Path outputDirectory = Paths.get("out").toAbsolutePath(); - JavacHolder javac = new JavacHolder(classPath, sourcePath, outputDirectory); + JavacHolder javac = new JavacHolder(classPath, sourcePath, outputDirectory, true); JavaLanguageServer server = new JavaLanguageServer(javac); InitializeParams init = new InitializeParams(); diff --git a/src/test/java/org/javacs/LinterTest.java b/src/test/java/org/javacs/LinterTest.java index 09d3422..88b0680 100644 --- a/src/test/java/org/javacs/LinterTest.java +++ b/src/test/java/org/javacs/LinterTest.java @@ -11,6 +11,7 @@ import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaFileObject; import java.io.IOException; +import java.net.URI; import java.nio.file.Paths; import java.util.*; import java.util.logging.Logger; @@ -18,133 +19,90 @@ import java.util.logging.Logger; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; -public class LinterTest extends Fixtures { +public class LinterTest { private static final Logger LOG = Logger.getLogger("main"); @Test public void compile() throws IOException { - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - GetResourceFileObject file = new GetResourceFileObject("/org/javacs/example/HelloWorld.java"); - JavacHolder compiler = newCompiler(); - compiler.onError(errors); - compiler.compile(Collections.singleton(compiler.parse(file))); + URI file = FindResource.uri("/org/javacs/example/HelloWorld.java"); + DiagnosticCollector<JavaFileObject> errors = newCompiler().compile(Collections.singletonMap(file, Optional.empty())).errors; assertThat(errors.getDiagnostics(), empty()); } @Test public void inspectTree() throws IOException { - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - GetResourceFileObject file = new GetResourceFileObject("/org/javacs/example/HelloWorld.java"); + URI file = FindResource.uri("/org/javacs/example/HelloWorld.java"); JavacHolder compiler = newCompiler(); CollectMethods scanner = new CollectMethods(compiler.context); - compiler.onError(errors); - - JCTree.JCCompilationUnit tree = compiler.parse(file); - - compiler.compile(Collections.singleton(tree)); - - tree.accept(scanner); + compiler.compile(Collections.singletonMap(file, Optional.empty())).trees.forEach(tree -> tree.accept(scanner)); assertThat(scanner.methodNames, hasItem("main")); } @Test public void missingMethodBody() throws IOException { - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - GetResourceFileObject file = new GetResourceFileObject("/org/javacs/example/MissingMethodBody.java"); + URI file = FindResource.uri("/org/javacs/example/MissingMethodBody.java"); JavacHolder compiler = newCompiler(); CollectMethods scanner = new CollectMethods(compiler.context); + CompilationResult compile = compiler.compile(Collections.singletonMap(file, Optional.empty())); - compiler.onError(errors); - - JCTree.JCCompilationUnit tree = compiler.parse(file); - - compiler.compile(Collections.singleton(tree)); - - tree.accept(scanner); + compile.trees.forEach(tree -> tree.accept(scanner)); assertThat(scanner.methodNames, hasItem("test")); - assertThat(errors.getDiagnostics(), not(empty())); + assertThat(compile.errors.getDiagnostics(), not(empty())); // Lint again - errors = new DiagnosticCollector<>(); - - compiler.onError(errors); - - tree = compiler.parse(file); + compile = compiler.compile(Collections.singletonMap(file, Optional.empty())); - compiler.compile(Collections.singleton(tree)); - - assertThat(errors.getDiagnostics(), not(empty())); + assertThat(compile.errors.getDiagnostics(), not(empty())); } @Test public void incompleteAssignment() throws IOException { - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - GetResourceFileObject file = new GetResourceFileObject("/org/javacs/example/IncompleteAssignment.java"); + URI file = FindResource.uri("/org/javacs/example/IncompleteAssignment.java"); JavacHolder compiler = newCompiler(); - CollectMethods parsed = new CollectMethods(compiler.context); CollectMethods compiled = new CollectMethods(compiler.context); + CompilationResult compile = compiler.compile(Collections.singletonMap(file, Optional.empty())); - compiler.onError(errors); - - JCTree.JCCompilationUnit tree = compiler.parse(file); + compile.trees.forEach(tree -> tree.accept(compiled)); - tree.accept(parsed); - - compiler.compile(Collections.singleton(tree)); - - tree.accept(compiled); - - assertThat(parsed.methodNames, hasItem("test")); // Error recovery should have worked assertThat(compiled.methodNames, hasItem("test")); // Type error recovery should have worked - assertThat(errors.getDiagnostics(), not(empty())); + assertThat(compile.errors.getDiagnostics(), not(empty())); } @Test public void undefinedSymbol() throws IOException { - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - GetResourceFileObject file = new GetResourceFileObject("/org/javacs/example/UndefinedSymbol.java"); + URI file = FindResource.uri("/org/javacs/example/UndefinedSymbol.java"); JavacHolder compiler = newCompiler(); CollectMethods scanner = new CollectMethods(compiler.context); + CompilationResult compile = compiler.compile(Collections.singletonMap(file, Optional.empty())); - compiler.onError(errors); - - JCTree.JCCompilationUnit tree = compiler.parse(file); - - compiler.compile(Collections.singleton(tree)); - - tree.accept(scanner); + compile.trees.forEach(tree -> tree.accept(scanner)); assertThat(scanner.methodNames, hasItem("test")); // Type error, so parse tree is present - Diagnostic<? extends JavaFileObject> d = errors.getDiagnostics().get(0); + assertThat(compile.errors.getDiagnostics(), not(empty())); + + Diagnostic<? extends JavaFileObject> d = compile.errors.getDiagnostics().get(0); // Error position should span entire 'foo' symbol assertThat(d.getLineNumber(), greaterThan(0L)); assertThat(d.getStartPosition(), greaterThan(0L)); assertThat(d.getEndPosition(), greaterThan(d.getStartPosition() + 1)); - assertThat(errors.getDiagnostics(), not(empty())); } @Test public void getType() { - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - GetResourceFileObject file = new GetResourceFileObject("/org/javacs/example/FooString.java"); + URI file = FindResource.uri("/org/javacs/example/FooString.java"); JavacHolder compiler = newCompiler(); MethodTypes scanner = new MethodTypes(compiler.context); + CompilationResult compile = compiler.compile(Collections.singletonMap(file, Optional.empty())); - compiler.onError(errors); - - JCTree.JCCompilationUnit tree = compiler.parse(file); - - compiler.compile(Collections.singleton(tree)); + compile.trees.forEach(tree -> tree.accept(scanner)); - tree.accept(scanner); - - assertThat(errors.getDiagnostics(), empty()); + assertThat(compile.errors.getDiagnostics(), empty()); assertThat(scanner.methodTypes, hasKey("test")); Type.MethodType type = scanner.methodTypes.get("test"); @@ -156,35 +114,29 @@ public class LinterTest extends Fixtures { @Test public void notJava() { - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - GetResourceFileObject file = new GetResourceFileObject("/org/javacs/example/NotJava.java.txt"); + URI file = FindResource.uri("/org/javacs/example/NotJava.java.txt"); JavacHolder compiler = newCompiler(); - compiler.onError(errors); - compiler.compile(Collections.singleton(compiler.parse(file))); + CompilationResult compile = compiler.compile(Collections.singletonMap(file, Optional.empty())); - assertThat(errors.getDiagnostics(), not(empty())); + assertThat(compile.errors.getDiagnostics(), not(empty())); } @Test public void errorInDependency() { - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - GetResourceFileObject file = new GetResourceFileObject("/org/javacs/example/ErrorInDependency.java"); + URI file = FindResource.uri("/org/javacs/example/ErrorInDependency.java"); JavacHolder compiler = newCompiler(); - compiler.onError(errors); - compiler.compile(Collections.singleton(compiler.parse(file))); + CompilationResult compile = compiler.compile(Collections.singletonMap(file, Optional.empty())); - assertThat(errors.getDiagnostics(), not(empty())); + assertThat(compile.errors.getDiagnostics(), not(empty())); } @Test public void deprecationWarning() { - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - GetResourceFileObject file = new GetResourceFileObject("/org/javacs/example/DeprecationWarning.java"); + URI file = FindResource.uri("/org/javacs/example/DeprecationWarning.java"); JavacHolder compiler = newCompiler(); - compiler.onError(errors); - compiler.compile(Collections.singleton(compiler.parse(file))); + CompilationResult compile = compiler.compile(Collections.singletonMap(file, Optional.empty())); - assertThat(errors.getDiagnostics(), not(empty())); + assertThat(compile.errors.getDiagnostics(), not(empty())); } public static class MethodTypes extends BaseScanner { @@ -222,8 +174,11 @@ public class LinterTest extends Fixtures { } private static JavacHolder newCompiler() { - return new JavacHolder(Collections.emptySet(), - Collections.singleton(Paths.get("src/test/resources")), - Paths.get("out")); + return new JavacHolder( + Collections.emptySet(), + Collections.singleton(Paths.get("src/test/resources")), + Paths.get("out"), + false + ); } } diff --git a/src/test/java/org/javacs/ParserTest.java b/src/test/java/org/javacs/ParserTest.java index 2a3074d..63a93f6 100644 --- a/src/test/java/org/javacs/ParserTest.java +++ b/src/test/java/org/javacs/ParserTest.java @@ -5,39 +5,41 @@ import com.sun.tools.javac.tree.TreeScanner; import org.junit.Test; import java.io.IOException; +import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; import static org.hamcrest.Matchers.hasItem; import static org.junit.Assert.assertThat; -public class ParserTest extends Fixtures { +public class ParserTest { @Test public void missingSemicolon() throws IOException, URISyntaxException { JavacHolder compiler = newCompiler(); List<String> methods = new ArrayList<>(); + URI file = FindResource.uri("/org/javacs/example/MissingSemicolon.java"); - GetResourceFileObject file = new GetResourceFileObject("/org/javacs/example/MissingSemicolon.java"); - - JCTree.JCCompilationUnit tree = compiler.parse(file); - - tree.accept(new TreeScanner() { + compiler.compile(Collections.singletonMap(file, Optional.empty())).trees.forEach(t -> t.accept(new TreeScanner() { @Override public void visitMethodDef(JCTree.JCMethodDecl node) { methods.add(node.getName().toString()); } - }); + })); assertThat(methods, hasItem("methodWithMissingSemicolon")); assertThat(methods, hasItem("methodAfterMissingSemicolon")); } private JavacHolder newCompiler() { - return new JavacHolder(Collections.emptySet(), - Collections.singleton(Paths.get("src/test/resources")), - Paths.get("out")); + return new JavacHolder( + Collections.emptySet(), + Collections.singleton(Paths.get("src/test/resources")), + Paths.get("out"), + false + ); } } diff --git a/src/test/java/org/javacs/RecompileTest.java b/src/test/java/org/javacs/RecompileTest.java index 15da1b5..bb7d123 100644 --- a/src/test/java/org/javacs/RecompileTest.java +++ b/src/test/java/org/javacs/RecompileTest.java @@ -7,135 +7,102 @@ import org.junit.Test; import javax.tools.DiagnosticCollector; import javax.tools.JavaFileObject; import java.io.IOException; -import java.nio.file.Path; +import java.net.URI; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; -public class RecompileTest extends Fixtures { - static { - Fixtures.init(); - } - +public class RecompileTest { @Test public void compileTwice() { - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - GetResourceFileObject file = new GetResourceFileObject("/org/javacs/example/CompileTwice.java"); + URI file = FindResource.uri("/org/javacs/example/CompileTwice.java"); JavacHolder compiler = newCompiler(); List<String> visits = new ArrayList<>(); GetClass getClass = new GetClass(compiler.context, visits); + CompilationResult compile = compiler.compile(Collections.singletonMap(file, Optional.empty())); - compiler.onError(errors); - - JCTree.JCCompilationUnit tree = compiler.parse(file); - - compiler.compile(Collections.singleton(tree)); - - tree.accept(getClass); + compile.trees.forEach(tree -> tree.accept(getClass)); - assertThat(errors.getDiagnostics(), empty()); + assertThat(compile.errors.getDiagnostics(), empty()); assertThat(visits, hasItems("CompileTwice", "NestedStaticClass", "NestedClass")); // Compile again - tree = compiler.parse(file); + compile = compiler.compile(Collections.singletonMap(file, Optional.empty())); - compiler.compile(Collections.singleton(tree)); + compile.trees.forEach(tree -> tree.accept(getClass)); - tree.accept(getClass); - - assertThat(errors.getDiagnostics(), empty()); + assertThat(compile.errors.getDiagnostics(), empty()); assertThat(visits, hasItems("CompileTwice", "NestedStaticClass", "NestedClass", "CompileTwice", "NestedStaticClass", "NestedClass")); } @Test public void fixParseError() { - Path path = Paths.get("org/javacs/example/FixParseError.java"); - StringFileObject bad = new StringFileObject("public class FixParseError { public String foo() { return \"foo\"; }", path); - StringFileObject good = new StringFileObject("public class FixParseError { public String foo() { return \"foo\"; } }", path); + URI bad = FindResource.uri("/org/javacs/example/FixParseErrorBefore.java"); + URI good = FindResource.uri("/org/javacs/example/FixParseErrorAfter.java"); JavacHolder compiler = newCompiler(); - DiagnosticCollector<JavaFileObject> badErrors = new DiagnosticCollector<>(); - - compiler.onError(badErrors); - compiler.parse(bad); - - DiagnosticCollector<JavaFileObject> goodErrors = new DiagnosticCollector<>(); + DiagnosticCollector<JavaFileObject> badErrors = compiler.compile(Collections.singletonMap(bad, Optional.empty())).errors; assertThat(badErrors.getDiagnostics(), not(empty())); // Parse again + CompilationResult goodCompile = compiler.compile(Collections.singletonMap(good, Optional.empty())); + DiagnosticCollector<JavaFileObject> goodErrors = goodCompile.errors; List<String> parsedClassNames = new ArrayList<>(); GetClass getClass = new GetClass(compiler.context, parsedClassNames); - compiler.onError(goodErrors); - - JCTree.JCCompilationUnit tree = compiler.parse(good); - - tree.accept(getClass); + goodCompile.trees.forEach(tree -> tree.accept(getClass)); assertThat(goodErrors.getDiagnostics(), empty()); - assertThat(parsedClassNames, contains("FixParseError")); + assertThat(parsedClassNames, contains("FixParseErrorAfter")); } @Test public void fixTypeError() { - Path path = Paths.get("org/javacs/example/FixTypeError.java"); - StringFileObject bad = new StringFileObject("public class FixTypeError { public String foo() { return 1; } }", path); - StringFileObject good = new StringFileObject("public class FixTypeError { public String foo() { return \"foo\"; } }", path); + URI bad = FindResource.uri("/org/javacs/example/FixTypeErrorBefore.java"); + URI good = FindResource.uri("/org/javacs/example/FixTypeErrorAfter.java"); JavacHolder compiler = newCompiler(); - DiagnosticCollector<JavaFileObject> badErrors = new DiagnosticCollector<>(); - - compiler.onError(badErrors); - compiler.compile(Collections.singleton(compiler.parse(bad))); - - DiagnosticCollector<JavaFileObject> goodErrors = new DiagnosticCollector<>(); + DiagnosticCollector<JavaFileObject> badErrors = compiler.compile(Collections.singletonMap(bad, Optional.empty())).errors; assertThat(badErrors.getDiagnostics(), not(empty())); // Parse again + CompilationResult goodCompile = compiler.compile(Collections.singletonMap(good, Optional.empty())); + DiagnosticCollector<JavaFileObject> goodErrors = goodCompile.errors; List<String> parsedClassNames = new ArrayList<>(); - GetClass getClass = new GetClass(compiler.context, parsedClassNames); - compiler.onError(goodErrors); - - JCTree.JCCompilationUnit tree = compiler.parse(good); - - compiler.compile(Collections.singleton(tree)); - - tree.accept(getClass); + goodCompile.trees.forEach(tree -> tree.accept(getClass)); assertThat(goodErrors.getDiagnostics(), empty()); - assertThat(parsedClassNames, contains("FixTypeError")); + assertThat(parsedClassNames, contains("FixTypeErrorAfter")); } private static JavacHolder newCompiler() { - return new JavacHolder(Collections.emptySet(), - Collections.singleton(Paths.get("src/test/resources")), - Paths.get("out")); + return new JavacHolder( + Collections.emptySet(), + Collections.singleton(Paths.get("src/test/resources")), + Paths.get("out"), + false + ); } @Test public void keepTypeError() throws IOException { - DiagnosticCollector<JavaFileObject> errors = new DiagnosticCollector<>(); - GetResourceFileObject file = new GetResourceFileObject("/org/javacs/example/UndefinedSymbol.java"); + URI file = FindResource.uri("/org/javacs/example/UndefinedSymbol.java"); JavacHolder compiler = newCompiler(); // Compile once - compiler.onError(errors); - compiler.compile(Collections.singleton(compiler.parse(file))); - + DiagnosticCollector<JavaFileObject> errors = compiler.compile(Collections.singletonMap(file, Optional.empty())).errors; assertThat(errors.getDiagnostics(), not(empty())); // Compile twice - errors = new DiagnosticCollector<>(); - compiler.onError(errors); - - compiler.compile(Collections.singleton(compiler.parse(file))); + errors = compiler.compile(Collections.singletonMap(file, Optional.empty())).errors; assertThat(errors.getDiagnostics(), not(empty())); } diff --git a/src/test/java/org/javacs/SymbolIndexTest.java b/src/test/java/org/javacs/SymbolIndexTest.java index cedbc5d..a6f35b7 100644 --- a/src/test/java/org/javacs/SymbolIndexTest.java +++ b/src/test/java/org/javacs/SymbolIndexTest.java @@ -1,14 +1,16 @@ package org.javacs; import com.sun.tools.javac.code.Symbol; - -import javax.tools.*; -import com.sun.tools.javac.tree.*; +import com.sun.tools.javac.tree.JCTree; import org.junit.Test; +import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -62,7 +64,7 @@ public class SymbolIndexTest { compile(path); - List<String> all = index.allInFile(new GetResourceFileObject(path).toUri()) + List<String> all = index.allInFile(FindResource.uri(path)) .map(s -> s.getName()) .collect(Collectors.toList()); @@ -82,7 +84,7 @@ public class SymbolIndexTest { compile(path); - List<String> all = index.allInFile(new GetResourceFileObject(path).toUri()) + List<String> all = index.allInFile(FindResource.uri(path)) .map(s -> s.getName()) .collect(Collectors.toList()); @@ -90,17 +92,14 @@ public class SymbolIndexTest { } private Symbol symbol(String path, int line, int character) { - return new JavaLanguageServer(compiler).findSymbol(compile(path), line, character).orElse(null); + return new JavaLanguageServer(compiler) + .findSymbol(FindResource.uri(path), line, character).orElse(null); } private JCTree.JCCompilationUnit compile(String path) { - JavaFileObject file = new GetResourceFileObject(path); - JCTree.JCCompilationUnit tree = compiler.parse(file); - - compiler.compile(Collections.singleton(tree)); - index.update(tree, compiler.context); + URI file = FindResource.uri(path); - return tree; + return compiler.compile(Collections.singletonMap(file, Optional.empty())).trees.stream().findFirst().get(); } private Set<String> search(String query) { @@ -111,20 +110,19 @@ public class SymbolIndexTest { private SymbolIndex index = getIndex(); private static SymbolIndex getIndex() { - Set<Path> sourcePath = Collections.singleton(Paths.get("src/main/java").toAbsolutePath()); - Path outputDirectory = Paths.get("out").toAbsolutePath(); - SymbolIndex index = new SymbolIndex(classPath, sourcePath, outputDirectory); - - index.initialIndexComplete.join(); + compiler.initialIndexComplete.join(); - return index; + return compiler.index; } private static JavacHolder compiler = newCompiler(); private static JavacHolder newCompiler() { - return new JavacHolder(Collections.emptySet(), - Collections.singleton(Paths.get("src/test/resources")), - Paths.get("out")); + return new JavacHolder( + Collections.emptySet(), + Collections.singleton(Paths.get("src/test/resources")), + Paths.get("out"), + true + ); } }
\ No newline at end of file diff --git a/src/test/java/org/javacs/SymbolUnderCursorTest.java b/src/test/java/org/javacs/SymbolUnderCursorTest.java index 0de053b..187372d 100644 --- a/src/test/java/org/javacs/SymbolUnderCursorTest.java +++ b/src/test/java/org/javacs/SymbolUnderCursorTest.java @@ -1,18 +1,16 @@ package org.javacs; import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.tree.JCTree; import org.junit.Ignore; import org.junit.Test; -import javax.tools.JavaFileObject; import java.nio.file.Paths; import java.util.Collections; import java.util.Optional; import static org.junit.Assert.assertEquals; -public class SymbolUnderCursorTest extends Fixtures { +public class SymbolUnderCursorTest { @Test public void classDeclaration() { @@ -81,25 +79,19 @@ public class SymbolUnderCursorTest extends Fixtures { } private String symbolAt(String file, int line, int character) { - Optional<Symbol> symbol = new JavaLanguageServer(compiler).findSymbol(compile(file), line, character); + Optional<Symbol> symbol = new JavaLanguageServer(compiler).findSymbol(FindResource.uri(file), line, character); return symbol.map(s -> s.getSimpleName().toString()).orElse(null); } - private JCTree.JCCompilationUnit compile(String path) { - JavaFileObject file = new GetResourceFileObject(path); - JCTree.JCCompilationUnit tree = compiler.parse(file); - - compiler.compile(Collections.singleton(tree)); - - return tree; - } - private static JavacHolder compiler = newCompiler(); private static JavacHolder newCompiler() { - return new JavacHolder(Collections.emptySet(), - Collections.singleton(Paths.get("src/test/resources")), - Paths.get("out")); + return new JavacHolder( + Collections.emptySet(), + Collections.singleton(Paths.get("src/test/resources")), + Paths.get("out"), + false + ); } } diff --git a/src/test/resources/org/javacs/example/FixParseErrorAfter.java b/src/test/resources/org/javacs/example/FixParseErrorAfter.java new file mode 100644 index 0000000..87449fb --- /dev/null +++ b/src/test/resources/org/javacs/example/FixParseErrorAfter.java @@ -0,0 +1 @@ +public class FixParseErrorAfter { public String foo() { return "foo"; } }
\ No newline at end of file diff --git a/src/test/resources/org/javacs/example/FixParseErrorBefore.java b/src/test/resources/org/javacs/example/FixParseErrorBefore.java new file mode 100644 index 0000000..32f6aae --- /dev/null +++ b/src/test/resources/org/javacs/example/FixParseErrorBefore.java @@ -0,0 +1 @@ +public class FixParseErrorBefore { public String foo() { return "foo"; }
\ No newline at end of file diff --git a/src/test/resources/org/javacs/example/FixTypeErrorAfter.java b/src/test/resources/org/javacs/example/FixTypeErrorAfter.java new file mode 100644 index 0000000..1b72b04 --- /dev/null +++ b/src/test/resources/org/javacs/example/FixTypeErrorAfter.java @@ -0,0 +1 @@ +public class FixTypeErrorAfter { public String foo() { return "foo"; } }
\ No newline at end of file diff --git a/src/test/resources/org/javacs/example/FixTypeErrorBefore.java b/src/test/resources/org/javacs/example/FixTypeErrorBefore.java new file mode 100644 index 0000000..a644d93 --- /dev/null +++ b/src/test/resources/org/javacs/example/FixTypeErrorBefore.java @@ -0,0 +1 @@ +public class FixTypeErrorBefore { public String foo() { return 1; } }
\ No newline at end of file |