[ 
https://issues.apache.org/jira/browse/GROOVY-12435?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18130108#comment-18130108
 ] 

Paul King commented on GROOVY-12435:
------------------------------------

AI read:

GROOVY-12435 is confirmed. It has been there since the antlr4 ("Parrot") parser 
arrived in Groovy 3.0, and it's a regression compared with the antlr2 parser. 
Wherever a line ends in a lone `\r`, line and column numbers after it are 
wrong, and stack traces and compile errors point at the wrong place.

## Reproduction

I rebuilt the attachment from its byte count rather than reading it directly, 
because the sandbox wouldn't let me read the saved copy. The key byte is a lone 
`\r` right after `package foo`; the rest uses CRLF.

| Case                                         | master, 5.0.6, 4.0.32, 3.0.25 
(antlr4) | 3.0.25 antlr2 | Correct |
| -------------------------------------------- | 
-------------------------------------- | ------------- | ------- |
| Issue shape: import, statement               | lines 2, 6                     
        | 3, 7          | 3, 7    |
| All-CR file, 3 statements                    | lines 1, 1, 1; columns 1, 11, 
22       | 1, 2, 4       | 1, 2, 4 |
| CRs inside a `/* */` comment, next statement | line 3                         
        | 5             | 5       |
| CR inside a `'''` string, next statement     | line 2                         
        | 3             | 3       |
| Runtime stack trace for a throw on line 3    | line 1                         
        | 3             | 3       |
| Syntax error on line 3, column 9             | line 1, column 29              
        | 3:9           | 3:9     |

Master gives exactly the reporter's numbers (2 and 6).

## Cause

- The grammar is fine: `LineTerminator : '\r'? '\n' | '\r'`, so a lone `\r` 
becomes a normal newline token.
- Line counting happens in the ANTLR runtime, which Groovy uses as the 
`me.sunlan:antlr4-runtime` 4.13.2.17 fork. Its 
`LexerATNSimulator.consume(input, curChar)` only does `line++` on `'\n'`. 
That's the upstream ANTLR behaviour Eric's comment links to.
- In the fork, that method is `final` and package-private, and the main DFA 
loop calls it directly. So overriding the public `consume(input)` in Groovy's 
existing `PositionAdjustingLexerATNSimulator` (in `GroovyLangLexer.java`) 
wouldn't help.
- A second, smaller bug: `PositionConfigureUtils.java:65` works out a node's 
end line by counting only `'\n'` in the last token. A multi-line string with 
lone CRs would still get a wrong end line even after the lexer is fixed.
- The rest of Groovy disagrees with the parser. `AbstractReaderSource.getLine` 
uses `BufferedReader.readLine()`, which does treat a lone `\r` as a line end. 
So today an error message's source excerpt shows a different line from the line 
number it reports.

## Fix options

**A. Fix the fork (cleanest).** In `consume(input, curChar)`, also count `'\r'` 
as a new line when `input.LA(2) != '\n'`. The extra look-ahead only happens on 
`\r` characters, so it costs essentially nothing. It needs a new fork release 
from Daniel, a version bump in `versions.properties`, and regenerated 
`verification-metadata.xml`. It deliberately departs from upstream ANTLR, which 
the fork already does for performance.

**B. Groovy-only.** Override `match(CharStream, int mode)` in 
`PositionAdjustingLexerATNSimulator`. After `super.match`, rescan the token 
just read for lone `\r` and correct `line` and `charPositionInLine`.
- `match` runs for every token, including skipped whitespace and comments, so 
start positions come out right.
- To keep the lexer's hot path untouched, check once per file whether it 
contains a lone `\r`, and skip the rescan entirely when it doesn't.
- No dependency release is needed, and it can be backported to 4.x and 5.x 
unchanged.

Either way, `PositionConfigureUtils` needs the matching change: count `\r\n`, a 
lone `\r`, and a lone `\n` each as one line end. Tests can follow the table: 
AST line and column numbers, a stack-trace line, and a syntax-error position, 
for lone-CR, mixed and all-CR files.

**Recommendation:** use B for 6.0.x and the backports, since it fixes this with 
no dependency change. Ask Daniel whether he'd take A in the fork; if he does, B 
can be removed later. This isn't a release blocker: lone-CR endings are rare 
(old Mac files and mixed-ending files from editors). But it's a real tooling 
problem, as the reporter found in his editor, and the stack-trace line numbers 
are wrong too.


> carriage-return only line ending does not increment line number
> ---------------------------------------------------------------
>
>                 Key: GROOVY-12435
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12435
>             Project: Groovy
>          Issue Type: Bug
>          Components: parser-antlr4
>    Affects Versions: 3.0.25, 4.0.33, 5.1.3, 6.0.0
>            Reporter: Eric Milles
>            Priority: Major
>         Attachments: LineEndings.groovy, image-2026-09-30-09-41-06-973.png
>
>
> [^LineEndings.groovy]I had a source file with mixed line endings that caused 
> a lot of trouble with the editor. I traced this back to the parser's handling 
> of line endings. It seems when a line ending is just a carriage-return (\r) 
> the line number is not incremented.
> !image-2026-09-30-09-41-06-973.png!
> In this example, the import and script statement have lines 2 and 6 
> respectively. They should be at lines 3 and 7 as can be seen by the line 
> numbering in the editor's gutter. The antlr2 parser handled this case 
> properly.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to