RFR: 8324859: Improve error recovery

Jan Lahoda jlahoda at openjdk.org
Mon Jul 8 10:09:00 UTC 2024


Consider code like:

package tests;
public class TestB {
    public static boolean test() // <--- missing open brace
        return true;
    }
    public static boolean test2() {
        return true;
    }
}


In JDK 17, it used to produce 3 compile-time errors:

$ javac  /tmp/TestB.java 
/tmp/TestB.java:3: error: ';' expected
    public static boolean test() // <--- missing open brace
                                ^
/tmp/TestB.java:6: error: class, interface, enum, or record expected
    public static boolean test2() {
                  ^
/tmp/TestB.java:8: error: class, interface, enum, or record expected
    }
    ^
3 errors


Currently, it produces 4:

$ javac  /tmp/TestB.java 
/tmp/TestB.java:3: error: ';' expected
    public static boolean test() // <--- missing open brace
                                ^
/tmp/TestB.java:6: error: implicitly declared classes are a preview feature and are disabled by default.
    public static boolean test2() {
                  ^
  (use --enable-preview to enable implicitly declared classes)
/tmp/TestB.java:9: error: class, interface, enum, or record expected
}
^
/tmp/TestB.java:1: error: implicitly declared class should not have package declaration
package tests;
^
4 errors


Neither of these is particularly nice. The common property is that the javac's parser "de-synchronizes" on the missing opening brace (`{`), and consumes the first closing brace (`}`) as a closing brace for the class (not for the method), causing all the follow-up behavior.

In general, the javac's parser handles missing closing braces fairly well - it skips tokens until it find something it can synchronize on, and continues. But, it does not handle missing opening braces as well, and has tendency to get lost in the input.

For the case above, it would be much better if javac parsed the code following the missing opening brace as a block.

This patch is attempting to do that, without making other cases I was able to find (much) worse. The overall approach is to skip tokens until a statement or member start is found, and then look at what follows:
- if it is an opening brace, parse the follow up as a block
- if it is a statement keyword (like `if`), parse the follow up as a block,
- if it is a closing brace, see if injecting a virtual opening brace at the current position would balance the opening/closing braces in the rest of the file - if yes, parse the follow up as a block,
- otherwise, try to speculatively parse the follow up - if it looks like a well-formed block, let the main parser parse it as a block.

There is a small trick by checking whether the parameters were parsed OK - and not do most of the above if there was a problem while parsing the parameters. This is to keep the existing behavior in the `testImplicitlyDeclaredClassesConfusion6` case.

-------------

Commit messages:
 - More cases.
 - Improving error message for attributes with default value.
 - Handling another testcase.
 - Merge branch 'master' into JDK-8324859
 - Tweaking the error recovery.
 - 8324859: Improve error recovery

Changes: https://git.openjdk.org/jdk/pull/20070/files
  Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=20070&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8324859
  Stats: 595 lines in 2 files changed: 592 ins; 1 del; 2 mod
  Patch: https://git.openjdk.org/jdk/pull/20070.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/20070/head:pull/20070

PR: https://git.openjdk.org/jdk/pull/20070


More information about the compiler-dev mailing list