j-mie6 / parsley

A fast and modern parser combinator library for Scala
https://j-mie6.github.io/parsley/
BSD 3-Clause "New" or "Revised" License
173 stars 15 forks source link

[BUG] Errors from `notFollowedBy` crash if it parses no input on failure #194

Closed j-mie6 closed 1 year ago

j-mie6 commented 1 year ago

Description

When notFollowedBy fails, it generates an error with the same width as the input it parsed. However, if it parses no input and fails, this will cause a crash, since the unexpected item is empty.

Reproduction Steps

scala> notFollowedBy(unit).parse("abc")
java.lang.StringIndexOutOfBoundsException: String index out of range: 0
  at java.base/java.lang.StringLatin1.charAt(StringLatin1.java:48)
  at java.base/java.lang.String.charAt(String.java:1513)
  at parsley.errors.helpers$WhitespaceOrUnprintable$.unapply(helpers.scala:33)
  at parsley.errors.helpers$.renderRawString(helpers.scala:15)
  at parsley.errors.DefaultErrorBuilder.raw(DefaultErrorBuilder.scala:124)
  at parsley.errors.DefaultErrorBuilder.raw(DefaultErrorBuilder.scala:124)
  at parsley.internal.errors.UnexpectRaw.formatUnexpect(ErrorItem.scala:26)
  at parsley.internal.errors.TrivialError.$anonfun$1(ParseError.scala:28)
  at scala.util.Either.map(Either.scala:382)
  at parsley.internal.errors.TrivialError.format(ParseError.scala:28)
  at parsley.internal.errors.ParseError.format(ParseError.scala:19)
  at parsley.internal.errors.ParseError.format$(ParseError.scala:8)
  at parsley.internal.errors.TrivialError.format(ParseError.scala:24)

Expected behavior

There is a law that tells us that notFollowedBy(pure(x)) = empty, so the error returned should in fact be:

(line 1, column 1):
  unknown parse error
  >abc

Additional context

Parsley Version: 4.2.11 Scala Version: 3.3.0

This is unlikely to come up in practice.