NPE when formatting java file with java 21 preview features.
Env
$ openjdk 21-ea 2023-09-19
OpenJDK Runtime Environment (build 21-ea+20-1677)
OpenJDK 64-Bit Server VM (build 21-ea+20-1677, mixed mode, sharing)
google-javaformat = "1.17.0"Error
> Task :spotlessJava FAILED Step 'google-java-format' found problem in 'src/main/java/xxx/DOP.java': 57:10: error: java.lang.NullPointerException: Cannot invoke "com.sun.tools.javac.tree.JCTree.getStartPosition()" because "node" is null at com.google.googlejavaformat.java.JavaInputAstVisitor.sync(JavaInputAstVisitor.java:3933) at com.google.googlejavaformat.java.JavaInputAstVisitor.visitToDeclare(JavaInputAstVisitor.java:2799) at com.google.googlejavaformat.java.JavaInputAstVisitor.visitEnhancedForLoop(JavaInputAstVisitor.java:791) at com.google.googlejavaformat.java.JavaInputAstVisitor.visitEnhancedForLoop(JavaInputAstVisitor.java:167) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCEnhancedForLoop.accept(JCTree.java:1248) at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:92) at com.google.googlejavaformat.java.JavaInputAstVisitor.scan(JavaInputAstVisitor.java:357) at com.google.googlejavaformat.java.JavaInputAstVisitor.visitStatements(JavaInputAstVisitor.java:2229) at com.google.googlejavaformat.java.JavaInputAstVisitor.methodBody(JavaInputAstVisitor.java:1550) at com.google.googlejavaformat.java.JavaInputAstVisitor.visitMethod(JavaInputAstVisitor.java:1537) at com.google.googlejavaformat.java.JavaInputAstVisitor.visitMethod(JavaInputAstVisitor.java:167) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:944) at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:92) at com.google.googlejavaformat.java.JavaInputAstVisitor.scan(JavaInputAstVisitor.java:357) at com.google.googlejavaformat.java.JavaInputAstVisitor.addBodyDeclarations(JavaInputAstVisitor.java:3760) at com.google.googlejavaformat.java.JavaInputAstVisitor.visitClassDeclaration(JavaInputAstVisitor.java:2044) at com.google.googlejavaformat.java.java17.Java17InputAstVisitor.visitClass(Java17InputAstVisitor.java:120) at com.google.googlejavaformat.java.java17.Java17InputAstVisitor.visitClass(Java17InputAstVisitor.java:51) at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:851) at jdk.compiler/com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:92) at com.google.googlejavaformat.java.JavaInputAstVisitor.scan(JavaInputAstVisitor.java:357)
I am not sure for which part it's failing. I was playing the java 21 preview features. So here is the sample file used in the task
Sample
import org.jspecify.annotations.NullMarked; import java.io.*; import java.lang.reflect.RecordComponent; import java.nio.file.Files; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import static java.lang.System.out; import static java.util.Objects.requireNonNull; public class DOP { public static void main(String[] args) throws Exception { run(); } public static void run() throws Exception { @NullMarked record Person(String name, int age) { } var future = new CompletableFuture<>(); var textBlock = """ This is text block This will join \ with the line : %s "quote" = "added" Escape Start \n \t \r \b \f end Space Escape-\s\s\s\s\s\s\s\s\s\s-end Regex \\S \\d \\D \\w \\W \\d+ Escape char: \u0020 \u00A0 \u2000 \u3000 \uFEFF \u200B \u200C \u200D \u2028 \u2029 END """ .formatted(new Person("Foo", 40)); future.complete(textBlock); out.println(future.get()); amberReflections(); recordPatterns(); genericRecordPattern(); serializeRecord(); } private static void recordPatterns() { record Point(int x, int y) { } var points = List.of(new Point(1, 2), new Point(3, 4), new Point(5, 6)); // Record pattern in enhanced for loop for (Point(var x, var y) : points) { out.println("Point: (" + x + ", " + y + ")"); } } interface Name<T> { } record FullName<T>(T firstName, T lastName) implements Name<T> { } private static void print(Name name) { var result = switch (name) { case FullName(var first, var last) -> first + ", " + last; default -> "Invalid name"; }; out.println(result); if (name instanceof FullName<?> f) { // out.println(f.firstName() + ", " + f.lastName()); } // Named record pattern is not supported if (name instanceof FullName(var first, var last)) { // out.println(first + ", " + last); } } private static void genericRecordPattern() { print(new FullName<>("Foo", "Bar")); print(new FullName<>(1, 2)); print(new FullName<>(10L, 20L)); } private static void amberReflections() { var sealedClazz = Result.class; out.println("Result (Interface) -> " + sealedClazz.isInterface()); out.println("Result (Sealed Class) -> " + sealedClazz.isSealed()); for (Class<?> permittedSubclass : sealedClazz.getPermittedSubclasses()) { out.println("\nPermitted Subclass : " + permittedSubclass.getName()); if (permittedSubclass.isRecord()) { out.println(permittedSubclass.getSimpleName() + " record components are,"); for (RecordComponent rc : permittedSubclass.getRecordComponents()) { out.println(rc); } } } } private static void serializeRecord() throws Exception { // Local record record Lang(String name, int year) implements Serializable { Lang { requireNonNull(name); if (year <= 0) { throw new IllegalArgumentException("Invalid year " + year); } } } var serialFile = Files.createTempFile("record-serial", "data").toFile(); serialFile.deleteOnExit(); try (var oos = new ObjectOutputStream(new FileOutputStream(serialFile))) { List<Record> recs = List.of( new Lang("Java", 25), new Lang("Kotlin", 10), (Record) Result.success(100) ); for (Record rec : recs) { out.println("Serializing record: " + rec); oos.writeObject(rec); } oos.writeObject(null); // EOF } try (var ois = new ObjectInputStream(new FileInputStream(serialFile))) { Object rec; while ((rec = ois.readObject()) != null) { var result = switch (rec) { case null -> "n/a"; case Lang l when l.year >= 20 -> l.toString(); case Lang(var name, var year) -> name; case Result<?> r -> "Result value: " + r.getOrNull(); default -> "Invalid serialized data. Expected Result, but found " + rec; }; out.println("Deserialized record: " + rec); out.println(result); } } results().forEach(r -> { var result = switch (r) { case null -> "n/a"; case Result.Success<?> s -> s.toString(); case Result.Failure<?> f -> f.toString(); }; out.println("Result (Sealed Type): " + result); }); } static List<Result<?>> results() { return Arrays.asList(getResult(5), getResult(25), getResult(-1)); } static Result<Number> getResult(long l) { // Unnecessary boxing required for boolean check in switch expression return switch (Long.valueOf(l)) { case Long s when s > 0 && s < 10 -> Result.success(s); case Long s when s > 10 -> Result.failure(new IllegalArgumentException(String.valueOf(s))); default -> null; }; } }