From 4b4d087a686e5c5b1993278216f61870f58761f6 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 12 Dec 2025 07:59:49 -0800 Subject: [PATCH] Consolidate uses of javac's parser in google-java-format PiperOrigin-RevId: 843691248 --- .../googlejavaformat/java/Formatter.java | 50 ++------------ .../java/FormatterException.java | 8 ++- .../java/RemoveUnusedImports.java | 52 ++------------ .../googlejavaformat/java/StringWrapper.java | 45 ++---------- .../google/googlejavaformat/java/Trees.java | 68 +++++++++++++++++++ 5 files changed, 87 insertions(+), 136 deletions(-) diff --git a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java index c81e69707..ff87eaab7 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Formatter.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Formatter.java @@ -14,10 +14,8 @@ package com.google.googlejavaformat.java; -import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Range; import com.google.common.collect.RangeSet; @@ -31,25 +29,14 @@ import com.google.googlejavaformat.Newlines; import com.google.googlejavaformat.Op; import com.google.googlejavaformat.OpsBuilder; -import com.sun.tools.javac.file.JavacFileManager; -import com.sun.tools.javac.parser.JavacParser; -import com.sun.tools.javac.parser.ParserFactory; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.Log; -import com.sun.tools.javac.util.Options; -import java.io.IOError; import java.io.IOException; -import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; -import javax.tools.SimpleJavaFileObject; -import javax.tools.StandardLocation; /** * This is google-java-format, a new Java formatter that follows the Google Java Style Guide quite @@ -112,40 +99,13 @@ public Formatter(JavaFormatterOptions options) { static void format(final JavaInput javaInput, JavaOutput javaOutput, JavaFormatterOptions options) throws FormatterException { Context context = new Context(); - DiagnosticCollector diagnostics = new DiagnosticCollector<>(); - context.put(DiagnosticListener.class, diagnostics); - Options.instance(context).put("allowStringFolding", "false"); - Options.instance(context).put("--enable-preview", "true"); - JCCompilationUnit unit; - JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8); - try { - fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, ImmutableList.of()); - } catch (IOException e) { - // impossible - throw new IOError(e); - } - SimpleJavaFileObject source = - new SimpleJavaFileObject(URI.create("source"), JavaFileObject.Kind.SOURCE) { - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - return javaInput.getText(); - } - }; - Log.instance(context).useSource(source); - ParserFactory parserFactory = ParserFactory.instance(context); - JavacParser parser = - parserFactory.newParser( - javaInput.getText(), - /* keepDocComments= */ true, - /* keepEndPos= */ true, - /* keepLineMap= */ true); - unit = parser.parseCompilationUnit(); - unit.sourcefile = source; + List> errorDiagnostics = new ArrayList<>(); + JCCompilationUnit unit = + Trees.parse( + context, errorDiagnostics, /* allowStringFolding= */ false, javaInput.getText()); javaInput.setCompilationUnit(unit); - Iterable> errorDiagnostics = - Iterables.filter(diagnostics.getDiagnostics(), Formatter::errorDiagnostic); - if (!Iterables.isEmpty(errorDiagnostics)) { + if (!errorDiagnostics.isEmpty()) { throw FormatterException.fromJavacDiagnostics(errorDiagnostics); } OpsBuilder builder = new OpsBuilder(javaInput, javaOutput); diff --git a/core/src/main/java/com/google/googlejavaformat/java/FormatterException.java b/core/src/main/java/com/google/googlejavaformat/java/FormatterException.java index 5ca939bbb..7fe67073a 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/FormatterException.java +++ b/core/src/main/java/com/google/googlejavaformat/java/FormatterException.java @@ -14,12 +14,12 @@ package com.google.googlejavaformat.java; +import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Locale.ENGLISH; import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import com.google.googlejavaformat.FormatterDiagnostic; import java.util.List; import java.util.regex.Pattern; @@ -49,9 +49,11 @@ public List diagnostics() { } public static FormatterException fromJavacDiagnostics( - Iterable> diagnostics) { + List> diagnostics) { return new FormatterException( - Iterables.transform(diagnostics, FormatterException::toFormatterDiagnostic)); + diagnostics.stream() + .map(FormatterException::toFormatterDiagnostic) + .collect(toImmutableList())); } private static FormatterDiagnostic toFormatterDiagnostic(Diagnostic input) { diff --git a/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java b/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java index a1c722b7e..18745a809 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java +++ b/core/src/main/java/com/google/googlejavaformat/java/RemoveUnusedImports.java @@ -19,12 +19,9 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.googlejavaformat.java.Trees.getEndPosition; import static java.lang.Math.max; -import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.base.CharMatcher; import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.google.common.collect.Range; import com.google.common.collect.RangeMap; @@ -43,9 +40,6 @@ import com.sun.source.util.TreePathScanner; import com.sun.source.util.TreeScanner; import com.sun.tools.javac.api.JavacTrees; -import com.sun.tools.javac.file.JavacFileManager; -import com.sun.tools.javac.parser.JavacParser; -import com.sun.tools.javac.parser.ParserFactory; import com.sun.tools.javac.tree.DCTree; import com.sun.tools.javac.tree.DCTree.DCReference; import com.sun.tools.javac.tree.JCTree; @@ -53,22 +47,14 @@ import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCImport; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.Log; -import com.sun.tools.javac.util.Options; -import java.io.IOError; -import java.io.IOException; import java.lang.reflect.Method; -import java.net.URI; +import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; -import javax.tools.SimpleJavaFileObject; -import javax.tools.StandardLocation; import org.jspecify.annotations.Nullable; /** @@ -231,38 +217,10 @@ public static String removeUnusedImports(final String contents) throws Formatter private static JCCompilationUnit parse(Context context, String javaInput) throws FormatterException { - DiagnosticCollector diagnostics = new DiagnosticCollector<>(); - context.put(DiagnosticListener.class, diagnostics); - Options.instance(context).put("--enable-preview", "true"); - Options.instance(context).put("allowStringFolding", "false"); - JCCompilationUnit unit; - JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8); - try { - fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, ImmutableList.of()); - } catch (IOException e) { - // impossible - throw new IOError(e); - } - SimpleJavaFileObject source = - new SimpleJavaFileObject(URI.create("source"), JavaFileObject.Kind.SOURCE) { - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - return javaInput; - } - }; - Log.instance(context).useSource(source); - ParserFactory parserFactory = ParserFactory.instance(context); - JavacParser parser = - parserFactory.newParser( - javaInput, - /* keepDocComments= */ true, - /* keepEndPos= */ true, - /* keepLineMap= */ true); - unit = parser.parseCompilationUnit(); - unit.sourcefile = source; - Iterable> errorDiagnostics = - Iterables.filter(diagnostics.getDiagnostics(), Formatter::errorDiagnostic); - if (!Iterables.isEmpty(errorDiagnostics)) { + List> errorDiagnostics = new ArrayList<>(); + JCTree.JCCompilationUnit unit = + Trees.parse(context, errorDiagnostics, /* allowStringFolding= */ false, javaInput); + if (!errorDiagnostics.isEmpty()) { // error handling is done during formatting throw FormatterException.fromJavacDiagnostics(errorDiagnostics); } diff --git a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java index 9d917cc4a..22110ad8a 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java +++ b/core/src/main/java/com/google/googlejavaformat/java/StringWrapper.java @@ -19,14 +19,12 @@ import static com.google.googlejavaformat.java.Trees.getEndPosition; import static com.google.googlejavaformat.java.Trees.getStartPosition; import static java.lang.Math.min; -import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.stream.Collectors.joining; import com.google.common.base.CharMatcher; import com.google.common.base.Strings; import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import com.google.common.collect.Range; import com.google.common.collect.TreeRangeMap; import com.google.googlejavaformat.Newlines; @@ -37,17 +35,9 @@ import com.sun.source.tree.Tree.Kind; import com.sun.source.util.TreePath; import com.sun.source.util.TreePathScanner; -import com.sun.tools.javac.file.JavacFileManager; -import com.sun.tools.javac.parser.JavacParser; -import com.sun.tools.javac.parser.ParserFactory; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.Log; -import com.sun.tools.javac.util.Options; import com.sun.tools.javac.util.Position; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.URI; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; @@ -56,11 +46,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import javax.tools.Diagnostic; -import javax.tools.DiagnosticCollector; -import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; -import javax.tools.SimpleJavaFileObject; -import javax.tools.StandardLocation; /** Wraps string literals that exceed the column limit. */ public final class StringWrapper { @@ -480,34 +466,11 @@ private static boolean needWrapping(int columnLimit, String input) { /** Parses the given Java source. */ private static JCTree.JCCompilationUnit parse(String source, boolean allowStringFolding) throws FormatterException { - DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + List> errorDiagnostics = new ArrayList<>(); Context context = new Context(); - context.put(DiagnosticListener.class, diagnostics); - Options.instance(context).put("--enable-preview", "true"); - Options.instance(context).put("allowStringFolding", Boolean.toString(allowStringFolding)); - JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8); - try { - fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, ImmutableList.of()); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - SimpleJavaFileObject sjfo = - new SimpleJavaFileObject(URI.create("source"), JavaFileObject.Kind.SOURCE) { - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) { - return source; - } - }; - Log.instance(context).useSource(sjfo); - ParserFactory parserFactory = ParserFactory.instance(context); - JavacParser parser = - parserFactory.newParser( - source, /* keepDocComments= */ true, /* keepEndPos= */ true, /* keepLineMap= */ true); - JCTree.JCCompilationUnit unit = parser.parseCompilationUnit(); - unit.sourcefile = sjfo; - Iterable> errorDiagnostics = - Iterables.filter(diagnostics.getDiagnostics(), Formatter::errorDiagnostic); - if (!Iterables.isEmpty(errorDiagnostics)) { + JCTree.JCCompilationUnit unit = + Trees.parse(context, errorDiagnostics, allowStringFolding, source); + if (!errorDiagnostics.isEmpty()) { // error handling is done during formatting throw FormatterException.fromJavacDiagnostics(errorDiagnostics); } diff --git a/core/src/main/java/com/google/googlejavaformat/java/Trees.java b/core/src/main/java/com/google/googlejavaformat/java/Trees.java index a16ce17c0..6b053771b 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Trees.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Trees.java @@ -14,6 +14,11 @@ package com.google.googlejavaformat.java; +import static com.google.googlejavaformat.java.Trees.getEndPosition; +import static com.google.googlejavaformat.java.Trees.getStartPosition; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.common.collect.ImmutableList; import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.CompoundAssignmentTree; @@ -24,13 +29,26 @@ import com.sun.source.tree.ParenthesizedTree; import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; +import com.sun.tools.javac.file.JavacFileManager; +import com.sun.tools.javac.parser.JavacParser; +import com.sun.tools.javac.parser.ParserFactory; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.Pretty; import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.Log; +import com.sun.tools.javac.util.Options; import java.io.IOError; import java.io.IOException; +import java.net.URI; +import java.util.List; import javax.lang.model.element.Name; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticListener; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardLocation; /** Utilities for working with {@link Tree}s. */ class Trees { @@ -118,4 +136,54 @@ static ClassTree getEnclosingTypeDeclaration(TreePath path) { static ExpressionTree skipParen(ExpressionTree node) { return ((ParenthesizedTree) node).getExpression(); } + + static JCCompilationUnit parse( + Context context, + List> errorDiagnostics, + boolean allowStringFolding, + String javaInput) { + DiagnosticListener diagnostics = + diagnostic -> { + if (errorDiagnostic(diagnostic)) { + errorDiagnostics.add(diagnostic); + } + }; + context.put(DiagnosticListener.class, diagnostics); + Options.instance(context).put("--enable-preview", "true"); + Options.instance(context).put("allowStringFolding", Boolean.toString(allowStringFolding)); + JavacFileManager fileManager = new JavacFileManager(context, /* register= */ true, UTF_8); + try { + fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, ImmutableList.of()); + } catch (IOException e) { + // impossible + throw new IOError(e); + } + SimpleJavaFileObject source = + new SimpleJavaFileObject(URI.create("source"), JavaFileObject.Kind.SOURCE) { + @Override + public String getCharContent(boolean ignoreEncodingErrors) { + return javaInput; + } + }; + Log.instance(context).useSource(source); + ParserFactory parserFactory = ParserFactory.instance(context); + JavacParser parser = + parserFactory.newParser( + javaInput, + /* keepDocComments= */ true, + /* keepEndPos= */ true, + /* keepLineMap= */ true); + JCCompilationUnit unit = parser.parseCompilationUnit(); + unit.sourcefile = source; + return unit; + } + + private static boolean errorDiagnostic(Diagnostic input) { + if (input.getKind() != Diagnostic.Kind.ERROR) { + return false; + } + // accept constructor-like method declarations that don't match the name of their + // enclosing class + return !input.getCode().equals("compiler.err.invalid.meth.decl.ret.type.req"); + } }