From 99707d885931bf67e49f142e944fd77ab76e0b17 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Fri, 24 Jun 2022 04:08:12 -0700 Subject: [PATCH] Avoid lookahead into interpolation Since an interpolation is really two tokens deep, for the literal part and the interpolated expression, one-char lookahead does not preserve the second token on reset. Make lookahead itself more robust by falling back to a LookaheadScanner for this case. Also add an internal error for the bad reset. --- .../dotty/tools/dotc/parsing/Scanners.scala | 18 +++++++++++++----- tests/pos/i15514.scala | 4 ++++ 2 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 tests/pos/i15514.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 12d6e34c1e98..70ca40412202 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -695,10 +695,10 @@ object Scanners { getNextToken(token) if token == END && !isEndMarker then token = IDENTIFIER - def reset() = { + def reset() = + if next.token != EMPTY && !isInstanceOf[LookaheadScanner] then report.error(s"Internal: lookAhead/reset would erase next token ${tokenString(next.token)} after ${tokenString(token)}", sourcePos()) next.copyFrom(this) this.copyFrom(prev) - } def closeIndented() = currentRegion match case r: Indented if !r.isOutermost => insert(OUTDENT, offset) @@ -1080,16 +1080,24 @@ object Scanners { // Lookahead --------------------------------------------------------------- /** The next token after this one. + * * The token is computed via fetchToken, so complex two word * tokens such as CASECLASS are not recognized. * Newlines and indent/unindent tokens are skipped. * + * Since interpolation prefetches both STRINGPART and an expression, + * use a scanner for rare case. Otherwise the next is overwritten on reset. */ - def lookahead: TokenData = - if next.token == EMPTY then + def lookahead: TokenData = + if next.token != EMPTY then next + else if token == INTERPOLATIONID then + val scan = LookaheadScanner() + scan.nextToken() + scan + else lookAhead() reset() - next + next class LookaheadScanner(val allowIndent: Boolean = false) extends Scanner(source, offset) { override def languageImportContext = Scanner.this.languageImportContext diff --git a/tests/pos/i15514.scala b/tests/pos/i15514.scala new file mode 100644 index 000000000000..2b9ed1dd31f4 --- /dev/null +++ b/tests/pos/i15514.scala @@ -0,0 +1,4 @@ + +object Main { s"Hello $Main.toStr!" } + +object Alt { s"Hello ${Alt}.toStr!" }