[
https://issues.apache.org/jira/browse/GROOVY-12169?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18097224#comment-18097224
]
ASF GitHub Bot commented on GROOVY-12169:
-----------------------------------------
testlens-app[bot] commented on PR #2718:
URL: https://github.com/apache/groovy/pull/2718#issuecomment-5008315909
## ✅ All tests passed ✅
🏷️ Commit: 04791b21276f6d654771e9978370cfbd0aeb901b
▶️ Tests: 104446 executed
⚪️ Checks: 31/31 completed
---
_Learn more about TestLens at [testlens.app](https://testlens.app)._
> Improve syntax error messages and caret positions for missing ')', ']', and
> '}'
> -------------------------------------------------------------------------------
>
> Key: GROOVY-12169
> URL: https://issues.apache.org/jira/browse/GROOVY-12169
> Project: Groovy
> Issue Type: Improvement
> Reporter: Daniel Sun
> Priority: Major
>
> h3. Problem
> When Groovy source is missing a closing delimiter (right paren, right
> bracket, or right brace), the ANTLR4 parser often reports a generic
> *Unexpected input* message at a *misleading* location (commonly the opening
> token or an earlier construct), instead of a clear *Missing ...* message at
> the place where the closer should be inserted.
> That makes simple typos hard to diagnose, especially with:
> * casts and parenthesised expressions (missing right paren)
> * lists, maps, and indexing (missing right bracket, including safe-index)
> * blocks, classes, and closures (missing right brace)
> h3. Examples (before)
> * Source:
> {noformat}
> println ((int 123)
> {noformat}
> Report: Unexpected input for open-paren at the *opening* parenthesis
> * Source:
> {noformat}
> [1, 2
> {noformat}
> Report: Unexpected input: '<EOF>' at end of input
> * Source:
> {noformat}
> foo[1
> {noformat}
> Report: Unexpected input for the open-bracket token
> * Source:
> {noformat}
> def m() {
> println 1
> {noformat}
> Report: Unexpected input far from the missing brace
> * Source:
> {noformat}
> foo([1, 2)
> {noformat}
> Report: Unexpected input for open-paren (real issue: missing right bracket
> before right paren)
> h3. Root cause
> * ANTLR4 does not recover missing tokens as helpfully as the old Antlr2 path.
> * Grammar-level error alternatives (for example on {{{}rparen{}}}) can yield
> a *Missing right-paren* message, but they enlarge the ATN and hurt
> {*}successful{*}-parse performance. They were removed in the GROOVY-9588 work
> for that reason.
> * With {{BailErrorStrategy}} plus generic mismatch reporting, prediction
> often abandons a deep delimited alternative and falls back to a shorter
> parse, so the surface error points far from the real omission.
> h3. Goal
> Report {*}Missing right-paren{*}, {*}Missing right-bracket{*}, or *Missing
> right-brace* with an accurate caret when the token stream clearly indicates a
> missing or incomplete closer — *without* reintroducing grammar error
> alternatives on the hot (successful) parse path.
> h3. Approach
> Error-path-only diagnostics wired into the existing error strategy:
> * {{DescriptiveErrorStrategy}} — on input mismatch / no viable alternative,
> prefer a missing-delimiter hit over the generic message.
> * {{MissingDelimiterDiagnostic}} — one linear scan of the fully filled token
> stream *after* failure; no work on successful parses.
> Detection order (most specific first):
> *1. Sole expected closer* — RecognitionException whose *sole* expected token
> is RPAREN, RBRACK, or RBRACE, and the corresponding open-depth is still
> positive. This avoids false positives where delimiters are balanced but an
> intermediate token is wrong, for example:
> {noformat}
> foo(1;2;3)
> {noformat}
> *2. Cast pattern* — open-paren, type, then expression-start without the
> cast's close-paren, for example:
> {noformat}
> (int 123
> {noformat}
> *3. Delimiter stack* — walk openers for paren, bracket, safe-index, and
> brace; report the *innermost* missing closer on mismatch or at EOF, for
> example:
> {noformat}
> foo([1, 2)
> {noformat}
> (missing right bracket before the right paren)
> The token stream is force-filled to EOF before scanning so a trailing closer
> is visible even when the parser failed earlier inside the construct.
> h3. Expected result (after)
> * Source:
> {noformat}
> println ((int 123)
> {noformat}
> Expected: *Missing right-paren* at 123
> * Source:
> {noformat}
> def x() {
> println((int) 123
> }
> {noformat}
> Expected: *Missing right-paren* after 123
> * Source:
> {noformat}
> def m( {
> }
> {noformat}
> Expected: *Missing right-paren* at the open brace
> * Source:
> {noformat}
> foo(1, 2
> {noformat}
> Expected: *Missing right-paren* at end of line
> * Source:
> {noformat}
> [1, 2
> {noformat}
> Expected: *Missing right-bracket* at end of line
> * Source:
> {noformat}
> foo[1
> {noformat}
> Expected: *Missing right-bracket* after 1
> * Source:
> {noformat}
> def x = [[1, 2]
> {noformat}
> Expected: *Missing right-bracket* after the inner closing bracket
> * Source:
> {noformat}
> foo([1, 2)
> {noformat}
> Expected: *Missing right-bracket* before the closing paren
> * Source:
> {noformat}
> a?[0
> {noformat}
> Expected: *Missing right-bracket* after 0
> * Source:
> {noformat}
> def m() {
> println 1
> {noformat}
> Expected: *Missing right-brace* after 1
> * Source:
> {noformat}
> class C {
> def x
> {noformat}
> Expected: *Missing right-brace* after x
> * Source:
> {noformat}
> def c = { it
> {noformat}
> Expected: *Missing right-brace* after it
> False-positive guard (delimiters balanced; must *not* report a
> Missing-delimiter message):
> {noformat}
> [].bar(1;2;3)
> {noformat}
> h3. Compatibility and performance
> * No grammar, lexer, or public API changes — valid programs parse as before.
> * Successful path remains {{BailErrorStrategy}} with no ATN error
> alternatives.
> * Extra work is a single O\(n\) token scan only after a recognition failure.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)