diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index e8ecf1c88f8c..3ac6f06432d2 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3957,8 +3957,8 @@ object Parsers { */ def selfType(): ValDef = if (in.isIdent || in.token == THIS) - && in.lookahead.isColon && followingIsSelfType() - || in.lookahead.token == ARROW + && (in.lookahead.isColon && followingIsSelfType() + || in.lookahead.token == ARROW) then atSpan(in.offset) { val selfName = diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 12d6e34c1e98..bf1fe4194c96 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -662,7 +662,7 @@ object Scanners { true else token == COLONfollow && (inTemplate || fewerBracesEnabled) if enabled then - lookAhead() + peekAhead() val atEOL = isAfterLineEnd || token == EOF reset() if atEOL then token = COLONeol @@ -690,15 +690,14 @@ object Scanners { insert(OUTDENT, offset) case _ => - def lookAhead() = + def peekAhead() = prev.copyFrom(this) getNextToken(token) if token == END && !isEndMarker then token = IDENTIFIER - def reset() = { + def reset() = next.copyFrom(this) this.copyFrom(prev) - } def closeIndented() = currentRegion match case r: Indented if !r.isOutermost => insert(OUTDENT, offset) @@ -717,12 +716,12 @@ object Scanners { } (token: @switch) match { case CASE => - lookAhead() + peekAhead() if (token == CLASS) fuse(CASECLASS) else if (token == OBJECT) fuse(CASEOBJECT) else reset() case SEMI => - lookAhead() + peekAhead() if (token != ELSE) reset() case COMMA => def isEnclosedInParens(r: Region): Boolean = r match @@ -733,7 +732,7 @@ object Scanners { case r: Indented if isEnclosedInParens(r.outer) => insert(OUTDENT, offset) case _ => - lookAhead() + peekAhead() if isAfterLineEnd && currentRegion.commasExpected && (token == RPAREN || token == RBRACKET || token == RBRACE || token == OUTDENT) @@ -1083,11 +1082,14 @@ object Scanners { * The token is computed via fetchToken, so complex two word * tokens such as CASECLASS are not recognized. * Newlines and indent/unindent tokens are skipped. - * + * Restriction: `lookahead` is illegal if the current token is INTERPOLATIONID */ - def lookahead: TokenData = + def lookahead: TokenData = if next.token == EMPTY then - lookAhead() + assert(token != INTERPOLATIONID) + // INTERPOLATONIDs are followed by a string literal, which can set next + // in peekAhead(). In that case, the following reset() would forget that token. + peekAhead() reset() next 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!" }