Skip to content

Commit 27148f2

Browse files
authored
fix(lexers): don't read past the end on an unterminated Raku comment (#1345)
## What's broken An unterminated bracketed Raku comment reads past the end of the input, and panics when the slice has no spare capacity. ``` lexers.Get("raku").Tokenise(nil, "#`(((unterminated") -> CommentMultiline "#`(((unterminated\x00\x00\x00" ``` Three NUL runes, matching the three-character opening delimiter. On an exactly-sized slice the same overshoot panics instead: ``` panic: runtime error: slice bounds out of range [:21] with capacity 20 ``` ## The fix When no closing bracket is found, `endPos` is already `len(text)`, but `nChars` still holds the count from the nesting loop, so `endPos+nChars` overshoots. The heredoc branch about forty lines above already guards this, clamping `endPos` and resetting `nChars` when the terminator is missing. This clamps the comment slice the same way. For a properly terminated comment `endPos+nChars` is already in bounds, so the clamp is a no-op and nothing changes. `state.Pos = endPos + nChars` on the next line has the same overshoot. It cannot panic today because the reader loop in `regexp.go` is guarded by `for l.Pos < end`, but it leaves `state.Pos` past the end of the text, so it is clamped too. ## Verification `lexers/testdata/raku/unterminated_comment.actual` and its golden, alongside the existing `unterminated_heredoc` pair. The `.expected` was generated with `RECORD=true go test`, not written by hand. Note that a brand new `.expected` has to exist as an empty file first, since `assert.NoError` on the read halts the subtest before the RECORD branch runs. With the clamp reverted the new test panics on the out-of-range slice above, so it is testing the real failure. `go test ./lexers/...` passes. --------- Co-authored-by: VXNCXNX <VXNCXNX@users.noreply.github.com>
1 parent 9943aa7 commit 27148f2

3 files changed

Lines changed: 11 additions & 2 deletions

File tree

lexers/raku.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -561,10 +561,15 @@ func rakuRules() Rules {
561561
}
562562
}
563563
default:
564-
state.Groups = []string{state.Groups[0] + string(text[state.Pos:endPos+nChars])}
564+
// If no closing delimiter was found, endPos is already
565+
// len(text); don't also add nChars on top of that, or
566+
// we'd read past the end of the input (same guard as
567+
// the heredoc-terminator handling above).
568+
commentEnd := min(endPos+nChars, len(text))
569+
state.Groups = []string{state.Groups[0] + string(text[state.Pos:commentEnd])}
565570
}
566571

567-
state.Pos = endPos + nChars
572+
state.Pos = min(endPos+nChars, len(text))
568573

569574
return nil
570575
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#`(((unterminated
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[
2+
{"type":"CommentMultiline","value":"#`(((unterminated\n"}
3+
]

0 commit comments

Comments
 (0)