Skip to content

Commit 95437d1

Browse files
committed
Better prediction after with in types
Also: generalize notion to get next token while suppressing indentation insertion
1 parent c45b7c9 commit 95437d1

File tree

4 files changed

+39
-21
lines changed

4 files changed

+39
-21
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,16 @@ object Parsers {
920920
val next = in.lookahead.token
921921
next == LBRACKET || next == LPAREN
922922

923+
private def withEndMigrationWarning(): Boolean =
924+
migrateTo3
925+
&& {
926+
warning(
927+
em"""In Scala 3, `with` at the end of a line will start definitions,
928+
|so it cannot be used in front of a parent constructor anymore.
929+
|Place the `with` at the beginning of the next line instead.""")
930+
true
931+
}
932+
923933
/** Does a template start after `with`? This is the case if either
924934
* - the next token is `{`
925935
* - the `with` is at the end of a line
@@ -931,17 +941,7 @@ object Parsers {
931941
val lookahead = in.LookaheadScanner()
932942
lookahead.nextToken()
933943
lookahead.token == LBRACE
934-
|| lookahead.isAfterLineEnd
935-
&& {
936-
if migrateTo3 then
937-
warning(
938-
em"""In Scala 3, `with` at the end of a line will start definitions,
939-
|so it cannot be used in front of a parent constructor anymore.
940-
|Place the `with` at the beginning of the next line instead.""")
941-
false
942-
else
943-
true
944-
}
944+
|| lookahead.isAfterLineEnd && !withEndMigrationWarning()
945945
|| (lookahead.isIdent || lookahead.token == THIS)
946946
&& {
947947
lookahead.nextToken()
@@ -953,6 +953,20 @@ object Parsers {
953953
|| lookahead.token == ARROW
954954
}
955955

956+
/** Does a refinement start after `with`? This is the case if either
957+
* - the next token is `{`
958+
* - the `with` is at the end of a line and is followed by a token that starts a declaration
959+
*/
960+
def followingIsRefinementStart() =
961+
val lookahead = in.LookaheadScanner()
962+
lookahead.nextToken()
963+
lookahead.token == LBRACE
964+
|| lookahead.isAfterLineEnd
965+
&& {
966+
if lookahead.token == INDENT then lookahead.nextToken()
967+
dclIntroTokens.contains(lookahead.token)
968+
}
969+
956970
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
957971

958972
var opStack: List[OpInfo] = Nil
@@ -1584,11 +1598,8 @@ object Parsers {
15841598
def withType(): Tree = withTypeRest(annotType())
15851599

15861600
def withTypeRest(t: Tree): Tree =
1587-
def isRefinementStart =
1588-
val la = in.lookahead
1589-
la.isAfterLineEnd || la.token == LBRACE
1590-
if in.token == WITH && !isRefinementStart then
1591-
in.nextToken()
1601+
if in.token == WITH && !followingIsRefinementStart() then
1602+
in.nextTokenNoIndent()
15921603
if sourceVersion.isAtLeast(`3.1`) then
15931604
deprecationWarning(DeprecatedWithOperator())
15941605
atSpan(startOffset(t)) { makeAndType(t, withType()) }
@@ -3858,8 +3869,7 @@ object Parsers {
38583869
if (name != nme.ERROR)
38593870
self = makeSelfDef(name, tpt).withSpan(first.span)
38603871
}
3861-
in.token = SELFARROW // suppresses INDENT insertion after `=>`
3862-
in.nextToken()
3872+
in.nextTokenNoIndent()
38633873
}
38643874
else {
38653875
stats += first

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,11 @@ object Scanners {
323323
printState()
324324
}
325325

326+
/** Like nextToken, but don't insert indent characters afterwards */
327+
def nextTokenNoIndent(): Unit =
328+
token = EMPTY // this will suppress newline and indent insertion
329+
nextToken()
330+
326331
final def printState() =
327332
if debugTokenStream && (showLookAheadOnDebug || !isInstanceOf[LookaheadScanner]) then
328333
print(s"[$show${if isInstanceOf[LookaheadScanner] then "(LA)" else ""}]")
@@ -493,8 +498,6 @@ object Scanners {
493498
if canStartIndentTokens.contains(lastToken) then
494499
currentRegion = Indented(nextWidth, Set(), lastToken, currentRegion)
495500
insert(INDENT, offset)
496-
else if lastToken == SELFARROW then
497-
currentRegion.knownWidth = nextWidth
498501
else if (lastWidth != nextWidth)
499502
errorButContinue(spaceTabMismatchMsg(lastWidth, nextWidth))
500503
currentRegion match {

compiler/src/dotty/tools/dotc/parsing/Tokens.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ object Tokens extends TokensCommon {
204204
final val QUOTE = 87; enter(QUOTE, "'")
205205

206206
final val COLONEOL = 88; enter(COLONEOL, ":", ": at eol")
207-
final val SELFARROW = 89; enter(SELFARROW, "=>") // reclassified ARROW following self-type
208207

209208
/** XML mode */
210209
final val XMLSTART = 99; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate

tests/neg/with-template.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,10 @@ object Test2 extends A with
1111
println("foo")
1212
}
1313

14+
def foo: A with
15+
B with
16+
C = ???
17+
18+
19+
1420

0 commit comments

Comments
 (0)