[ 
https://issues.apache.org/jira/browse/GROOVY-12353?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Paul King resolved GROOVY-12353.
--------------------------------
    Fix Version/s: 6.0.0-RC-2
         Assignee: Daniel Sun
       Resolution: Fixed

> Improve remaining common syntax error messages (unclosed literals, unexpected 
> characters, missing punctuation, reserved keywords)
> ---------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12353
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12353
>             Project: Groovy
>          Issue Type: Improvement
>            Reporter: Daniel Sun
>            Assignee: Daniel Sun
>            Priority: Major
>             Fix For: 6.0.0-RC-2
>
>
> h3. Problem
> After GROOVY-12169 and GROOVY-12171, several everyday syntax mistakes still 
> produce a generic *Unexpected input* or *Unexpected character* message that 
> does not name the actual problem.
> Invisible or look-alike characters (zero-width space, BOM, NUL, no-break 
> space, curly quotes, non-ASCII dashes) render as an empty or misleading glyph 
> between quotes. Unclosed quotes and comments are reported as an unexpected 
> quote or slash rather than as an unclosed literal. {{if true}} without 
> parentheses, {{x ? y}}, and {{const x = 1}} look like random unexpected 
> tokens instead of a missing {{(}} / {{:}} or an unimplemented keyword. Groovy 
> 4 safe index is a single token that pairs with a closing bracket, but an 
> unclosed index inside parentheses was reported as {{Missing ')'}}.
> h3. Examples (before)
> * Unclosed string:
> {noformat}
> println 'Hello
> {noformat}
> reports:
> {noformat}
> Unexpected character: ''' @ line 1, column 9.
> {noformat}
> (caret on the opening quote)
> * Unclosed block comment:
> {noformat}
> /* comment
> {noformat}
> reports:
> {noformat}
> Unexpected input: '/'
> {noformat}
> * Invisible character ({{def}} + U+200B + {{name = null}}):
> {noformat}
> Unexpected character: ''
> {noformat}
> * Missing punctuation:
> {noformat}
> if true { x = 1 }
> {noformat}
> reports:
> {noformat}
> Unexpected input: 'true'
> {noformat}
> * Reserved keyword:
> {noformat}
> const x = 1
> {noformat}
> reports:
> {noformat}
> Unexpected input: 'const'
> {noformat}
> * Duplicate location:
> {noformat}
> def n = 1_
> {noformat}
> reports:
> {noformat}
> Number ending with underscores is invalid @ line 1, column 10 @ line 1, 
> column 10.
> {noformat}
> * Hard-coded varargs name:
> {noformat}
> def m(int... a, int b) {}
> {noformat}
> reports:
> {noformat}
> The var-arg parameter strs must be the last parameter
> {noformat}
> * Unclosed safe index inside parentheses:
> {noformat}
> (a?[0
> {noformat}
> reports:
> {noformat}
> Missing ')'
> {noformat}
> h3. Root cause
> * Lexer {{UNEXPECTED_CHAR}} inlined the raw character with only a 
> quote-escape, so control, format, and look-alike characters vanish or 
> mislead. An unexpected quote is almost always an unclosed string, but the 
> message never said so.
> * Unclosed block comments failed the comment rule and were retokenized as 
> {{/}}, so the parser saw an unexpected slash.
> * Parser fallback wording was still ANTLR's *Unexpected input*, even when the 
> expected set was a single punctuation token, or the offender was a reserved 
> or misplaced keyword ({{const}}, {{goto}}, {{threadsafe}}, {{else}}, 
> {{catch}}, {{finally}}, {{case}}) or EOF. For {{default:}} / {{default ->}} 
> outside a switch, the offender is {{:}} or {{->}}, not {{default}}.
> * The missing-closer diagnostic trusted a sole expected closer. For an 
> unclosed safe index inside parentheses the parser expected {{)}}, so the 
> inner unclosed index was reported as {{Missing ')'}}. The safe-index token 
> was not treated as the same opener family as a normal index.
> * Lexer {{require(..., true)}} appended {{@ line N, column M}} to 
> {{GroovySyntaxError}}, and {{SyntaxException}} appended the same location 
> again.
> * {{AstBuilder}} used a hard-coded parameter name {{strs}} in the 
> varargs-not-last diagnostic.
> Grammar-level parser error alternatives are not an option: GROOVY-9588 showed 
> they enlarge the ATN and slow successful parses. An EOF closer on slashy 
> strings is also not an option: {{/}} after an expression with newlines is 
> division, not an unclosed slashy string.
> h3. Goal
> Give javac-aligned, developer-facing sentences for these common mistakes, 
> with an accurate caret, without reintroducing parser error alternatives on 
> the hot path.
> h3. Approach
> Error-path-only, two layers. Successful parses never enter these helpers.
> * Lexer ({{GroovyLexer.g4}} / {{AbstractLexer}}): an unexpected quote becomes 
> *Unclosed string literal*; an unclosed block comment becomes *Unclosed 
> comment* at the opener (one non-greedy loop, then the closer or EOF — not a 
> second lexer rule, which would win by longest match and swallow trailing 
> source). Other unexpected characters go through {{getCharErrorDisplay}} 
> (shared with the GString {{$}} path from GROOVY-12171). Unicode spaces other 
> than U+0020, curly quotes, and non-ASCII dashes are named as a Unicode escape 
> (for example {{'\u200b'}}) so they do not vanish into the caret line. Stop 
> attaching position text on lexer {{require}} calls so {{SyntaxException}} is 
> the only source of {{@ line N, column M}}.
> * Parser ({{AbstractFriendlyErrorStrategy}}): {{MissingDelimiterDiagnostic}} 
> still relocates the caret for a missing closer (GROOVY-12169). Safe index is 
> the same opener family as a normal index; if the innermost opener is not the 
> family of the sole expected closer, defer so an unclosed index inside 
> parentheses reports *Missing ']'*. Everything else only refines the fallback 
> sentence and keeps ANTLR's offending token: reserved/misplaced keyword, then 
> {{default:}} / {{default ->}} lookback (skip newlines; do not label an 
> incomplete interface {{default}} method as outside switch), then a leading 
> safe-index token without a receiver, then a singleton expected punctuation 
> token ({{Missing '('}}, {{Missing ':'}}, {{Missing '>'}}, ...), then 
> *Unexpected end of input* for EOF. Locate/refine stay in a defensive try; 
> listener dispatch is a single call afterwards.
> {{AstBuilder}} reports the actual varargs parameter name.
> h3. Expected result (after)
> * unclosed single-, double-, or triple-quoted string becomes *Unclosed string 
> literal*
> * unclosed block comment becomes *Unclosed comment* (caret on the opener)
> * zero-width space, no-break space, curly quote, em dash become {{Unexpected 
> character: '\u200b'}} (and the matching escape)
> * {{if true}} / {{while true}} / {{for int i in ...}} without parentheses 
> becomes {{Missing '('}}
> * {{x ? y}} becomes {{Missing ':'}}
> * a generic type missing {{>}} before {{(}} becomes {{Missing '>'}}
> * {{const x = 1}} becomes {{'const' is not supported; use 'val' or 'static 
> final' instead}}
> * {{goto label}} becomes {{'goto' is not supported}}
> * {{threadsafe}} as a modifier becomes {{'threadsafe' is not supported}}
> * stray {{else}} / {{catch}} / {{case}} become {{'else' without 'if'}} / 
> {{'catch' without 'try'}} / {{'case' outside of switch}}
> * {{default: x}} / {{default -> x}} at script scope becomes {{'default' 
> outside of switch}}; an incomplete interface {{default}} method is not that 
> message
> * unclosed or mismatched safe index, including inside parentheses, becomes 
> *Missing ']'*
> * a safe index with no receiver, or {{?}} immediately before one, becomes:
> {noformat}
> '?[' requires an expression before it
> {noformat}
> * a space between the question mark and the opening bracket is an incomplete 
> ternary ({{Missing ':'}}); safe index is one token:
> {noformat}
> a? [0]
> {noformat}
> * {{throw}} at EOF becomes *Unexpected end of input*
> * {{def n = 1_}} becomes *Number ending with underscores is invalid* 
> (position once)
> * {{def m(int... a, int b)}} becomes {{The var-arg parameter a must be the 
> last parameter}}
> Valid programs are unchanged.
> h3. Related
> GROOVY-12169, GROOVY-12171, GROOVY-10146



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to