From f5f92bf22df67151d719dbcbfe9fdf39c0d7e014 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Sat, 6 Dec 2025 08:31:39 -0800 Subject: [PATCH 1/9] Internal change PiperOrigin-RevId: 841130473 --- .../google/googlejavaformat/java/filer/FormattingFiler.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/google/googlejavaformat/java/filer/FormattingFiler.java b/core/src/main/java/com/google/googlejavaformat/java/filer/FormattingFiler.java index f5cc7fd09..e8da9fac9 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/filer/FormattingFiler.java +++ b/core/src/main/java/com/google/googlejavaformat/java/filer/FormattingFiler.java @@ -58,7 +58,8 @@ public static Filer create(ProcessingEnvironment processingEnv) { * @deprecated prefer {@link #create(ProcessingEnvironment)} */ @Deprecated - public FormattingFiler(Filer delegate) { + public + FormattingFiler(Filer delegate) { this(delegate, null); } @@ -71,7 +72,8 @@ public FormattingFiler(Filer delegate) { * @deprecated prefer {@link #create(ProcessingEnvironment)} */ @Deprecated - public FormattingFiler(Filer delegate, @Nullable Messager messager) { + public + FormattingFiler(Filer delegate, @Nullable Messager messager) { this.delegate = checkNotNull(delegate); this.messager = messager; } From 4b4d087a686e5c5b1993278216f61870f58761f6 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Fri, 12 Dec 2025 07:59:49 -0800 Subject: [PATCH 2/9] 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"); + } } From 737b0032b3a18eb6e458271ea440098c166f6c2d Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Mon, 19 Jan 2026 23:38:25 -0800 Subject: [PATCH 3/9] Support Instance Main Methods in google-java-format https://github.com/google/google-java-format/issues/1216 PiperOrigin-RevId: 858436442 --- .../googlejavaformat/java/JavaInputAstVisitor.java | 13 +++++++++++++ .../java/FormatterIntegrationTest.java | 2 +- .../java/testdata/InstanceMain.input | 5 +++++ .../java/testdata/InstanceMain.output | 5 +++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 core/src/test/resources/com/google/googlejavaformat/java/testdata/InstanceMain.input create mode 100644 core/src/test/resources/com/google/googlejavaformat/java/testdata/InstanceMain.output diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java index a2a32e79c..2f0373ec6 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java @@ -453,8 +453,15 @@ protected void dropEmptyDeclarations() { } } + // Replace with Flags.IMPLICIT_CLASS once JDK 25 is the minimum supported version + private static final int IMPLICIT_CLASS = 1 << 19; + @Override public Void visitClass(ClassTree tree, Void unused) { + if ((TreeInfo.flags((JCTree) tree) & IMPLICIT_CLASS) == IMPLICIT_CLASS) { + visitImplicitClass(tree); + return null; + } switch (tree.getKind()) { case ANNOTATION_TYPE -> visitAnnotationType(tree); case CLASS, INTERFACE -> visitClassDeclaration(tree); @@ -465,6 +472,12 @@ public Void visitClass(ClassTree tree, Void unused) { return null; } + private void visitImplicitClass(ClassTree node) { + builder.open(minusTwo); + addBodyDeclarations(node.getMembers(), BracesOrNot.NO, FirstDeclarationsOrNot.YES); + builder.close(); + } + public void visitAnnotationType(ClassTree node) { sync(node); builder.open(ZERO); diff --git a/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java b/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java index a406487f3..7b3c3e889 100644 --- a/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java +++ b/core/src/test/java/com/google/googlejavaformat/java/FormatterIntegrationTest.java @@ -59,7 +59,7 @@ public class FormatterIntegrationTest { "I981", "I1020", "I1037") - .putAll(25, "ModuleImport") + .putAll(25, "ModuleImport", "InstanceMain") .build(); @Parameters(name = "{index}: {0}") diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/InstanceMain.input b/core/src/test/resources/com/google/googlejavaformat/java/testdata/InstanceMain.input new file mode 100644 index 000000000..5967d6c66 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/InstanceMain.input @@ -0,0 +1,5 @@ +String greeting = "Hello, World!"; + +void main() { + System.out.println(greeting); +} diff --git a/core/src/test/resources/com/google/googlejavaformat/java/testdata/InstanceMain.output b/core/src/test/resources/com/google/googlejavaformat/java/testdata/InstanceMain.output new file mode 100644 index 000000000..6f152da61 --- /dev/null +++ b/core/src/test/resources/com/google/googlejavaformat/java/testdata/InstanceMain.output @@ -0,0 +1,5 @@ +String greeting = "Hello, World!"; + +void main() { + System.out.println(greeting); +} From 62edae4f12bd44e9ddd0e453438e774adf9e91b7 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Tue, 27 Jan 2026 07:21:34 -0800 Subject: [PATCH 4/9] Remove workaround for https://bugs.openjdk.org/browse/JDK-8027682 The bug was fixed in JDK 21, and google-java-format no longer supports earlier versions The corresponding tests were removed in https://github.com/google/google-java-format/commit/18f835849551f81d60d582300a0a3c585b5774b4 PiperOrigin-RevId: 861709714 --- .../google/googlejavaformat/java/JavaInputAstVisitor.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java index 2f0373ec6..66cb9d31b 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java +++ b/core/src/main/java/com/google/googlejavaformat/java/JavaInputAstVisitor.java @@ -410,11 +410,6 @@ public Void visitCompilationUnit(CompilationUnitTree node, Void unused) { } dropEmptyDeclarations(); for (Tree type : node.getTypeDecls()) { - if (type.getKind() == Tree.Kind.IMPORT) { - // javac treats extra semicolons in the import list as type declarations - // TODO(cushon): remove this if https://bugs.openjdk.java.net/browse/JDK-8027682 is fixed - continue; - } if (afterFirstToken) { builder.blankLineWanted(BlankLineWanted.YES); } @@ -1244,8 +1239,6 @@ public Void visitImport(ImportTree node, Void unused) { } visitName(node.getQualifiedIdentifier()); token(";"); - // TODO(cushon): remove this if https://bugs.openjdk.java.net/browse/JDK-8027682 is fixed - dropEmptyDeclarations(); return null; } From 4a15b1b9a4063a8ec2958ba3fc0924d1be046b93 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Thu, 29 Jan 2026 05:07:06 -0800 Subject: [PATCH 5/9] Prepare for end position API changes in https://bugs.openjdk.org/browse/JDK-8372948 PiperOrigin-RevId: 862686243 --- .../google/googlejavaformat/java/Trees.java | 110 ++++++++++++++++-- 1 file changed, 101 insertions(+), 9 deletions(-) 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 6b053771b..6a3f2a933 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Trees.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Trees.java @@ -14,10 +14,9 @@ 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.base.Throwables; import com.google.common.collect.ImmutableList; import com.sun.source.tree.ClassTree; import com.sun.source.tree.CompilationUnitTree; @@ -41,6 +40,10 @@ import com.sun.tools.javac.util.Options; import java.io.IOError; import java.io.IOException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.VarHandle; import java.net.URI; import java.util.List; import javax.lang.model.element.Name; @@ -49,6 +52,7 @@ import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardLocation; +import org.jspecify.annotations.Nullable; /** Utilities for working with {@link Tree}s. */ class Trees { @@ -69,7 +73,12 @@ static int getEndPosition(Tree expression, TreePath path) { /** Returns the source end position of the node. */ public static int getEndPosition(Tree tree, CompilationUnitTree unit) { - return ((JCTree) tree).getEndPosition(((JCCompilationUnit) unit).endPositions); + try { + return (int) GET_END_POS_HANDLE.invokeExact((JCTree) tree, (JCCompilationUnit) unit); + } catch (Throwable e) { + Throwables.throwIfUnchecked(e); + throw new AssertionError(e); + } } /** Returns the source text for the node. */ @@ -167,12 +176,20 @@ public String getCharContent(boolean ignoreEncodingErrors) { }; Log.instance(context).useSource(source); ParserFactory parserFactory = ParserFactory.instance(context); - JavacParser parser = - parserFactory.newParser( - javaInput, - /* keepDocComments= */ true, - /* keepEndPos= */ true, - /* keepLineMap= */ true); + JavacParser parser; + try { + parser = + (JavacParser) + NEW_PARSER_HANDLE.invokeExact( + parserFactory, + (CharSequence) javaInput, + /* keepDocComments */ true, + /* keepEndPos */ true, + /* keepLineMap */ true); + } catch (Throwable e) { + Throwables.throwIfUnchecked(e); + throw new AssertionError(e); + } JCCompilationUnit unit = parser.parseCompilationUnit(); unit.sourcefile = source; return unit; @@ -186,4 +203,79 @@ private static boolean errorDiagnostic(Diagnostic input) { // enclosing class return !input.getCode().equals("compiler.err.invalid.meth.decl.ret.type.req"); } + + private static final @Nullable Class END_POS_TABLE_CLASS = getEndPosTableClass(); + + private static @Nullable Class getEndPosTableClass() { + try { + return Class.forName("com.sun.tools.javac.tree.EndPosTable"); + } catch (ClassNotFoundException e) { + // JDK versions after https://bugs.openjdk.org/browse/JDK-8372948 + return null; + } + } + + private static final MethodHandle NEW_PARSER_HANDLE = getNewParserHandle(); + + private static MethodHandle getNewParserHandle() { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + if (END_POS_TABLE_CLASS == null) { + try { + // (parserFactory, input, keepDocComments, keepEndPos, keepLineMap) -> + // parserFactory.newParser(input, keepDocComments, keepLineMap) + return MethodHandles.dropArguments( + lookup.findVirtual( + ParserFactory.class, + "newParser", + MethodType.methodType( + JavacParser.class, CharSequence.class, boolean.class, boolean.class)), + 2, + boolean.class); + } catch (ReflectiveOperationException e) { + throw new LinkageError(e.getMessage(), e); + } + } + try { + // (parserFactory, input, keepDocComments, keepEndPos, keepLineMap) -> + // parserFactory.newParser(input, keepDocComments, keepEndPos, keepLineMap) + return lookup.findVirtual( + ParserFactory.class, + "newParser", + MethodType.methodType( + JavacParser.class, CharSequence.class, boolean.class, boolean.class, boolean.class)); + } catch (ReflectiveOperationException e) { + throw new LinkageError(e.getMessage(), e); + } + } + + private static final MethodHandle GET_END_POS_HANDLE = getEndPosMethodHandle(); + + private static MethodHandle getEndPosMethodHandle() { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + if (END_POS_TABLE_CLASS == null) { + try { + // (tree, unit) -> tree.getEndPosition() + return MethodHandles.dropArguments( + lookup.findVirtual(JCTree.class, "getEndPosition", MethodType.methodType(int.class)), + 1, + JCCompilationUnit.class); + } catch (ReflectiveOperationException e1) { + throw new LinkageError(e1.getMessage(), e1); + } + } + try { + // (tree, unit) -> tree.getEndPosition(unit.endPositions) + return MethodHandles.filterArguments( + lookup.findVirtual( + JCTree.class, + "getEndPosition", + MethodType.methodType(int.class, END_POS_TABLE_CLASS)), + 1, + lookup + .findVarHandle(JCCompilationUnit.class, "endPositions", END_POS_TABLE_CLASS) + .toMethodHandle(VarHandle.AccessMode.GET)); + } catch (ReflectiveOperationException e) { + throw new LinkageError(e.getMessage(), e); + } + } } From 9b9797344c77da9a6df0552407d2ce98e2a1bc4f Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Tue, 3 Feb 2026 01:08:25 -0800 Subject: [PATCH 6/9] Fix index of `keepEndPos` parameter PiperOrigin-RevId: 864745710 --- core/src/main/java/com/google/googlejavaformat/java/Trees.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6a3f2a933..faca6ff86 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Trees.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Trees.java @@ -229,7 +229,7 @@ private static MethodHandle getNewParserHandle() { "newParser", MethodType.methodType( JavacParser.class, CharSequence.class, boolean.class, boolean.class)), - 2, + 3, boolean.class); } catch (ReflectiveOperationException e) { throw new LinkageError(e.getMessage(), e); From f0a9748d045d54f3ebb78511bafc358cea6feb31 Mon Sep 17 00:00:00 2001 From: Liam Miller-Cushon Date: Wed, 4 Feb 2026 01:02:41 -0800 Subject: [PATCH 7/9] Update workaround for JDK-8372948 The upstream change is removing the overload of `newParser`. PiperOrigin-RevId: 865273867 --- .../google/googlejavaformat/java/Trees.java | 59 ++++++------------- 1 file changed, 19 insertions(+), 40 deletions(-) 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 faca6ff86..ad8201bae 100644 --- a/core/src/main/java/com/google/googlejavaformat/java/Trees.java +++ b/core/src/main/java/com/google/googlejavaformat/java/Trees.java @@ -179,13 +179,12 @@ public String getCharContent(boolean ignoreEncodingErrors) { JavacParser parser; try { parser = - (JavacParser) - NEW_PARSER_HANDLE.invokeExact( - parserFactory, - (CharSequence) javaInput, - /* keepDocComments */ true, - /* keepEndPos */ true, - /* keepLineMap */ true); + newParser( + parserFactory, + javaInput, + /* keepDocComments= */ true, + /* keepEndPos= */ true, + /* keepLineMap= */ true); } catch (Throwable e) { Throwables.throwIfUnchecked(e); throw new AssertionError(e); @@ -195,6 +194,19 @@ public String getCharContent(boolean ignoreEncodingErrors) { return unit; } + private static JavacParser newParser( + ParserFactory parserFactory, + CharSequence source, + boolean keepDocComments, + boolean keepEndPos, + boolean keepLineMap) { + if (END_POS_TABLE_CLASS != null) { + return parserFactory.newParser(source, keepDocComments, keepEndPos, keepLineMap); + } + return parserFactory.newParser( + source, keepDocComments, keepLineMap, /* parseModuleInfo */ false); + } + private static boolean errorDiagnostic(Diagnostic input) { if (input.getKind() != Diagnostic.Kind.ERROR) { return false; @@ -215,39 +227,6 @@ private static boolean errorDiagnostic(Diagnostic input) { } } - private static final MethodHandle NEW_PARSER_HANDLE = getNewParserHandle(); - - private static MethodHandle getNewParserHandle() { - MethodHandles.Lookup lookup = MethodHandles.lookup(); - if (END_POS_TABLE_CLASS == null) { - try { - // (parserFactory, input, keepDocComments, keepEndPos, keepLineMap) -> - // parserFactory.newParser(input, keepDocComments, keepLineMap) - return MethodHandles.dropArguments( - lookup.findVirtual( - ParserFactory.class, - "newParser", - MethodType.methodType( - JavacParser.class, CharSequence.class, boolean.class, boolean.class)), - 3, - boolean.class); - } catch (ReflectiveOperationException e) { - throw new LinkageError(e.getMessage(), e); - } - } - try { - // (parserFactory, input, keepDocComments, keepEndPos, keepLineMap) -> - // parserFactory.newParser(input, keepDocComments, keepEndPos, keepLineMap) - return lookup.findVirtual( - ParserFactory.class, - "newParser", - MethodType.methodType( - JavacParser.class, CharSequence.class, boolean.class, boolean.class, boolean.class)); - } catch (ReflectiveOperationException e) { - throw new LinkageError(e.getMessage(), e); - } - } - private static final MethodHandle GET_END_POS_HANDLE = getEndPosMethodHandle(); private static MethodHandle getEndPosMethodHandle() { From ff157e3e4655c695b59028bb2a2f322e992eae95 Mon Sep 17 00:00:00 2001 From: Claudio Nave Date: Wed, 4 Feb 2026 11:29:34 -0800 Subject: [PATCH 8/9] Fix reflection errors during native execution Hi, after updating to the new version 1.34.0 I'm receiving this error running the native binary: ``` error: no such method: com.sun.tools.javac.tree.JCTree.getEndPosition(EndPosTable)int/invokeVirtual java.lang.LinkageError: no such method: com.sun.tools.javac.tree.JCTree.getEndPosition(EndPosTable)int/invokeVirtual at com.google.googlejavaformat.java.Trees.getEndPosMethodHandle(Trees.java:278) at com.google.googlejavaformat.java.Trees.(Trees.java:251) at com.google.googlejavaformat.java.Formatter.format(Formatter.java:104) at com.google.googlejavaformat.java.Formatter.getFormatReplacements(Formatter.java:214) at com.google.googlejavaformat.java.Formatter.formatSource(Formatter.java:188) at com.google.googlejavaformat.java.FormatFileCallable.call(FormatFileCallable.java:75) at com.google.googlejavaformat.java.FormatFileCallable.call(FormatFileCallable.java:29) at java.base@25.0.2/java.util.concurrent.FutureTask.run(FutureTask.java:328) at java.base@25.0.2/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545) at java.base@25.0.2/java.util.concurrent.FutureTask.run(FutureTask.java:328) at java.base@25.0.2/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090) at java.base@25.0.2/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614) at java.base@25.0.2/java.lang.Thread.runWith(Thread.java:1487) at java.base@25.0.2/java.lang.Thread.run(Thread.java:1474) at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:832) at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:808) Caused by: java.lang.NoSuchMethodException: no such method: com.sun.tools.javac.tree.JCTree.getEndPosition(EndPosTable)int/invokeVirtual at java.base@25.0.2/java.lang.invoke.MemberName.makeAccessException(MemberName.java:910) at java.base@25.0.2/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:989) at java.base@25.0.2/java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:3591) at java.base@25.0.2/java.lang.invoke.MethodHandles$Lookup.findVirtual(MethodHandles.java:2610) at com.google.googlejavaformat.java.Trees.getEndPosMethodHandle(Trees.java:269) ... 15 more Caused by: java.lang.NoSuchMethodError: com.sun.tools.javac.tree.JCTree.getEndPosition(com.sun.tools.javac.tree.EndPosTable) at org.graalvm.nativeimage.builder/com.oracle.svm.core.methodhandles.Util_java_lang_invoke_MethodHandleNatives.resolve(Target_java_lang_invoke_MethodHandleNatives.java:352) at java.base@25.0.2/java.lang.invoke.MethodHandleNatives.resolve(MethodHandleNatives.java:208) at java.base@25.0.2/java.lang.invoke.MemberName$Factory.resolve(MemberName.java:120) at java.base@25.0.2/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:986) ... 18 more ``` It's caused by https://github.com/google/google-java-format/commit/4a15b1b9a4063a8ec2958ba3fc0924d1be046b93 that introduced new reflection invocations that the native build process isn't able to pick up. Following [this guide](https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/) I generated an updated config file to fix the problem. The config file name changed in Graal 23, but it's only used during build time so it shouldn't matter that much. With this fix everything works fine (at least for my case :grin:). Fixes #1327 COPYBARA_INTEGRATE_REVIEW=https://github.com/google/google-java-format/pull/1327 from EvaristeGalois11:fix-graalvm 6bdd7cd27aea5b8132f16b5ac2fd4f0ab0909273 PiperOrigin-RevId: 865501302 --- .../native-image/reachability-metadata.json | 106 ++++++++++++++++++ .../META-INF/native-image/reflect-config.json | 6 - 2 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 core/src/main/resources/META-INF/native-image/reachability-metadata.json delete mode 100644 core/src/main/resources/META-INF/native-image/reflect-config.json diff --git a/core/src/main/resources/META-INF/native-image/reachability-metadata.json b/core/src/main/resources/META-INF/native-image/reachability-metadata.json new file mode 100644 index 000000000..1047784a0 --- /dev/null +++ b/core/src/main/resources/META-INF/native-image/reachability-metadata.json @@ -0,0 +1,106 @@ +{ + "reflection": [ + { + "type": "com.google.googlejavaformat.java.JavacTokens$CommentSavingTokenizer" + }, + { + "type": "com.sun.source.tree.CaseTree" + }, + { + "type": "com.sun.source.tree.ImportTree", + "methods": [ + { + "name": "isModule", + "parameterTypes": [] + } + ] + }, + { + "type": "com.sun.tools.javac.parser.JavaTokenizer" + }, + { + "type": "com.sun.tools.javac.parser.ParserFactory", + "methods": [ + { + "name": "newParser", + "parameterTypes": [ + "java.lang.CharSequence", + "boolean", + "boolean", + "boolean" + ] + } + ] + }, + { + "type": "com.sun.tools.javac.parser.UnicodeReader", + "methods": [ + { + "name": "getRawCharacters", + "parameterTypes": [ + "int", + "int" + ] + } + ] + }, + { + "type": "com.sun.tools.javac.tree.EndPosTable" + }, + { + "type": "com.sun.tools.javac.tree.JCTree", + "methods": [ + { + "name": "getEndPosition", + "parameterTypes": [ + "com.sun.tools.javac.tree.EndPosTable" + ] + } + ] + }, + { + "type": "com.sun.tools.javac.tree.JCTree$JCCompilationUnit", + "fields": [ + { + "name": "endPositions" + } + ] + }, + { + "type": "com.sun.tools.javac.tree.JCTree$JCImport", + "methods": [ + { + "name": "getQualifiedIdentifier", + "parameterTypes": [] + } + ] + }, + { + "type": "com.sun.tools.javac.util.Log$DeferredDiagnosticHandler", + "methods": [ + { + "name": "", + "parameterTypes": [ + "com.sun.tools.javac.util.Log" + ] + }, + { + "name": "getDiagnostics", + "parameterTypes": [] + } + ] + }, + { + "type": "java.lang.Boolean", + "jniAccessible": true, + "methods": [ + { + "name": "getBoolean", + "parameterTypes": [ + "java.lang.String" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/core/src/main/resources/META-INF/native-image/reflect-config.json b/core/src/main/resources/META-INF/native-image/reflect-config.json deleted file mode 100644 index 2c6580345..000000000 --- a/core/src/main/resources/META-INF/native-image/reflect-config.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "name": "com.sun.tools.javac.parser.UnicodeReader", - "allDeclaredMethods": true - } -] From 64ee627337507dca8953da3771413fd81c96ec50 Mon Sep 17 00:00:00 2001 From: cushon Date: Wed, 4 Feb 2026 19:33:38 +0000 Subject: [PATCH 9/9] Release google-java-format 1.34.1 --- core/pom.xml | 2 +- eclipse_plugin/META-INF/MANIFEST.MF | 2 +- eclipse_plugin/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 3b106cf71..390f5c79b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -22,7 +22,7 @@ com.google.googlejavaformat google-java-format-parent - HEAD-SNAPSHOT + 1.34.1 google-java-format diff --git a/eclipse_plugin/META-INF/MANIFEST.MF b/eclipse_plugin/META-INF/MANIFEST.MF index 4170ad643..9676df6a9 100644 --- a/eclipse_plugin/META-INF/MANIFEST.MF +++ b/eclipse_plugin/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: google-java-format Bundle-SymbolicName: google-java-format-eclipse-plugin;singleton:=true Bundle-Vendor: Google -Bundle-Version: 1.31.0 +Bundle-Version: 1.34.1 Bundle-RequiredExecutionEnvironment: JavaSE-11 Require-Bundle: org.eclipse.jdt.core;bundle-version="3.10.0", org.eclipse.jface, diff --git a/eclipse_plugin/pom.xml b/eclipse_plugin/pom.xml index a5d8c23ae..59c49783f 100644 --- a/eclipse_plugin/pom.xml +++ b/eclipse_plugin/pom.xml @@ -22,7 +22,7 @@ com.google.googlejavaformat google-java-format-eclipse-plugin eclipse-plugin - 1.31.0 + 1.34.1 Google Java Format Plugin for Eclipse 4.5+ diff --git a/pom.xml b/pom.xml index 46306c196..7fc046bf7 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ com.google.googlejavaformat google-java-format-parent pom - HEAD-SNAPSHOT + 1.34.1 core