Daniel Sun created GROOVY-12401:
-----------------------------------
Summary: Improve remaining parser diagnostics and authored AST
source positions
Key: GROOVY-12401
URL: https://issues.apache.org/jira/browse/GROOVY-12401
Project: Groovy
Issue Type: Improvement
Reporter: Daniel Sun
h3. Problem
After GROOVY-12353, several everyday syntax mistakes still surface as generic
*Unexpected input*, and several authored AST nodes still carry line/column
{{-1}} even though they came from source. Tooling uses {{lineNumber > 0}} to
tell authored code from generated code ({{SecureASTCustomizer.isFromSource}}).
h3. Diagnostics (before)
* Array creation without a size, an extra empty dimension, or a size combined
with an initializer:
{noformat}
new int[]
new int[][2]
new int[2] { 1 }
{noformat}
reports *Unexpected input*.
* A type argument list written as a comparison (meant {noformat}List<Integer>
name{noformat}):
{noformat}
List<Integer name
{noformat}
reports *Unexpected input*.
* A method header in a statement position (for example inside a closure invoked
as a statement) reports *Unexpected input* instead of naming the misplaced
method.
* {{import}} inside a method reports {{Unexpected input: 'import'}}.
* {{do}} with two statements and a later {{while}}:
{noformat}
do
println 1
println 2
while (false)
{noformat}
reports {{Missing 'while'}} on the second {{println}} even though {{while}} is
already on the next line.
* Enum constants followed by members without a semicolon:
{noformat}
enum E { A
void m() {}
}
{noformat}
reports *Unexpected input* instead of asking for {{;}} after the last constant.
* An extra closer or semicolon reports {{Unexpected input: ')'}} (or the
matching {{]}} / {{\}}} / {{;}}) instead of naming the extra token.
* A second {{default}} in a switch points the caret at the first {{default}},
not the duplicate.
* Integer overflow wording does not match javac's {{Integer number too large}}.
h3. Source positions (before)
At CONVERSION these authored nodes dump as {{-1}}:
* Interface field {{int a;}} — {{FieldNode.lastLineNumber}} is copied from a
synthetic {{ConstantExpression(0)}}. {{DefaultGroovyMethods.asBoolean(Object)}}
is non-null, so the synthetic value is treated as a real stop.
* Classic for-control {noformat}for (int i = 0; i < n; i++){noformat} — the
outer {{ClosureListExpression}} is never given a source span. Empty
init/cond/update slots ({{EmptyExpression.INSTANCE}}) should stay {{-1}}.
* Authored empty parentheses:
{noformat}
new Object()
new Runnable() { }
super()
{noformat}
* GROOVY-11492 annotation default — the {{listX}} rewrite drops the brace span:
{noformat}
String[] x() default { }
{noformat}
* Single-spread index wrapper (the multi-index path was already stamped):
{noformat}
a[*[1, 2]]
{noformat}
Not a bug — these have no span in the source and should stay {{-1}}:
{{ModuleNode}}, a script's wrapping {{BlockStatement}}, implicit {{return
null}}, TWR desugaring (names such as {noformat}__$$t0{noformat}), synthetic
{{@Trait}}, the class initializer {noformat}<clinit>{noformat}, and
{{EmptyExpression.INSTANCE}}.
h3. Goal
* javac-aligned sentences for the remaining recognition failures, with the
caret on the right token
* authored AST nodes carry a real source span; generated nodes stay at {{-1}}
* error-path only — no grammar error alternatives (GROOVY-9588)
h3. Approach
* {{AbstractFriendlyErrorStrategy}} refines the fallback sentence: array
creation, unclosed type argument, method header in statement position,
misplaced {{import}}, unmatched {{do}}/{{while}}, enum semicolon, extra
closers, annotation-style {{default}} after a method header.
{{MissingDelimiterDiagnostic}} still relocates the caret for an unclosed {{)}}
/ {{]}} / {{\}}}. Locate/refine stay in a defensive try; listener dispatch is
one call afterwards.
* {{AstBuilder}} / {{ModifierManager}} / {{Numbers}}: record headers, missing
method body vs abstract, index used as a method call, overflow wording;
duplicate {{default}} points at the second {{default}}.
* {{PositionConfigureUtils}}: copy an end position only when {{lastLineNumber >
0}}. Stamp the classic-for control list, authored empty parentheses, the
GROOVY-11492 {{listX}} result, and the single-spread index wrapper.
h3. Expected result (after)
* {noformat}new int[]{noformat} → {noformat}Array dimension missing; specify a
size or add an initializer '{}'{noformat}
* {noformat}List<Integer name{noformat} → {{Missing '>'}}
* method header in a statement position → {{Method definition not expected
here}}
* {{import}} inside a method → {{'import' is only allowed at the beginning of a
compilation unit}}
* {{do}} / two statements / later {{while}} → {noformat}do-while body must be a
single statement; wrap multiple statements in '{ }'{noformat}; a true omission
is still {{Missing 'while'}}
* enum constants then members, no semicolon → {{';' expected after the last
enum constant}}
* extra {{)}} → {{Unexpected ')'}}
* interface {{int a;}}, empty {noformat}for (;;) { }{noformat}, {{new
Object()}}, annotation {noformat}default { }{noformat}, and {noformat}a[*[1,
2]]{noformat} carry real line/column on the authored node; synthetic children
stay {{-1}}
Valid programs are unchanged.
h3. Related
GROOVY-12353, GROOVY-11492, GROOVY-12169, GROOVY-9588
--
This message was sent by Atlassian Jira
(v8.20.10#820010)