Skip to content

Commit d834abe

Browse files
committed
Make treatment of do language version dependent
`do` can start an expression or a statement only under -language:Scala2. Maintaining this distinction is important in order not to insert spurious newlines in front of `do` in a `while` or `for`.
1 parent 93f2b7e commit d834abe

File tree

5 files changed

+59
-45
lines changed

5 files changed

+59
-45
lines changed

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

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ object Parsers {
207207
} && !in.isSoftModifierInModifierPosition
208208

209209
def isExprIntro: Boolean =
210-
canStartExpressionTokens.contains(in.token) && !in.isSoftModifierInModifierPosition
210+
in.canStartExprTokens.contains(in.token) && !in.isSoftModifierInModifierPosition
211211

212212
def isDefIntro(allowedMods: BitSet, excludedSoftModifiers: Set[TermName] = Set.empty): Boolean =
213213
in.token == AT ||
@@ -1231,6 +1231,10 @@ object Parsers {
12311231
// note: next is defined here because current == NEWLINE
12321232
if (in.token == NEWLINE && in.next.token == token) in.nextToken()
12331233

1234+
def newLinesOptWhenFollowedBy(name: Name): Unit =
1235+
if in.isNewLine && in.next.token == IDENTIFIER && in.next.name == name then
1236+
in.nextToken()
1237+
12341238
def newLineOptWhenFollowing(p: Int => Boolean): Unit =
12351239
// note: next is defined here because current == NEWLINE
12361240
if (in.token == NEWLINE && p(in.next.token)) newLineOpt()
@@ -1246,7 +1250,7 @@ object Parsers {
12461250
}
12471251

12481252
def possibleTemplateStart(): Unit = {
1249-
in.observeIndented(unless = noIndentTemplateTokens, unlessSoftKW = nme.derives)
1253+
in.observeIndented()
12501254
newLineOptWhenFollowedBy(LBRACE)
12511255
}
12521256

@@ -1651,7 +1655,7 @@ object Parsers {
16511655
def toBeContinued(altToken: Token): Boolean =
16521656
if in.token == altToken || in.isNewLine || in.isScala2Mode then
16531657
false // a newline token means the expression is finished
1654-
else if !canStartStatTokens.contains(in.token)
1658+
else if !in.canStartStatTokens.contains(in.token)
16551659
|| in.isLeadingInfixOperator(inConditional = true)
16561660
then
16571661
true
@@ -1852,20 +1856,20 @@ object Parsers {
18521856
}
18531857
}
18541858
case _ =>
1855-
if (isIdent(nme.inline) && !in.inModifierPosition() && in.lookaheadIn(canStartExpressionTokens)) {
1859+
if isIdent(nme.inline)
1860+
&& !in.inModifierPosition()
1861+
&& in.lookaheadIn(in.canStartExprTokens)
1862+
then
18561863
val start = in.skipToken()
1857-
in.token match {
1864+
in.token match
18581865
case IF =>
18591866
ifExpr(start, InlineIf)
18601867
case _ =>
18611868
val t = postfixExpr()
18621869
if (in.token == MATCH) matchExpr(t, start, InlineMatch)
1863-
else {
1870+
else
18641871
syntaxErrorOrIncomplete(i"`match` or `if` expected but ${in.token} found")
18651872
t
1866-
}
1867-
}
1868-
}
18691873
else expr1Rest(postfixExpr(), location)
18701874
}
18711875

@@ -2012,7 +2016,7 @@ object Parsers {
20122016
def postfixExpr(): Tree = postfixExprRest(prefixExpr())
20132017

20142018
def postfixExprRest(t: Tree): Tree =
2015-
infixOps(t, canStartExpressionTokens, prefixExpr, maybePostfix = true)
2019+
infixOps(t, in.canStartExprTokens, prefixExpr, maybePostfix = true)
20162020

20172021
/** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr
20182022
*/
@@ -2209,7 +2213,7 @@ object Parsers {
22092213
lookahead.nextToken()
22102214
lookahead.token != COLON
22112215
}
2212-
else canStartExpressionTokens.contains(lookahead.token)
2216+
else in.canStartExprTokens.contains(lookahead.token)
22132217
}
22142218
}
22152219
if (in.token == LPAREN && (!inClassConstrAnnots || isLegalAnnotArg))
@@ -2338,7 +2342,7 @@ object Parsers {
23382342
dropParensOrBraces(start, if (in.token == YIELD || in.token == DO) "" else "do")
23392343
}
23402344
}
2341-
in.observeIndented(unless = noIndentAfterEnumeratorTokens)
2345+
in.observeIndented()
23422346
res
23432347
}
23442348
else {
@@ -2484,7 +2488,7 @@ object Parsers {
24842488
/** InfixPattern ::= SimplePattern {id [nl] SimplePattern}
24852489
*/
24862490
def infixPattern(): Tree =
2487-
infixOps(simplePattern(), canStartExpressionTokens, simplePattern, isOperator = in.name != nme.raw.BAR)
2491+
infixOps(simplePattern(), in.canStartExprTokens, simplePattern, isOperator = in.name != nme.raw.BAR)
24882492

24892493
/** SimplePattern ::= PatVar
24902494
* | Literal
@@ -3470,6 +3474,7 @@ object Parsers {
34703474
/** Template ::= InheritClauses [TemplateBody]
34713475
*/
34723476
def template(constr: DefDef, isEnum: Boolean = false): Template = {
3477+
newLinesOptWhenFollowedBy(nme.derives)
34733478
val (parents, derived) = inheritClauses()
34743479
possibleTemplateStart()
34753480
if (isEnum) {
@@ -3482,10 +3487,11 @@ object Parsers {
34823487
/** TemplateOpt = [Template]
34833488
*/
34843489
def templateOpt(constr: DefDef): Template =
3485-
possibleTemplateStart()
3490+
newLinesOptWhenFollowedBy(nme.derives)
34863491
if in.token == EXTENDS || isIdent(nme.derives) then
34873492
template(constr)
34883493
else
3494+
possibleTemplateStart()
34893495
if in.isNestedStart then
34903496
template(constr)
34913497
else

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

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ object Scanners {
385385
lookahead.allowLeadingInfixOperators = false
386386
// force a NEWLINE a after current token if it is on its own line
387387
lookahead.nextToken()
388-
canStartExpressionTokens.contains(lookahead.token)
388+
canStartExprTokens.contains(lookahead.token)
389389
}
390390
&& {
391391
if isScala2Mode || oldSyntax && !rewrite then
@@ -495,10 +495,11 @@ object Scanners {
495495
indentPrefix = LBRACE
496496
case _ =>
497497
}
498-
if (newlineIsSeparating &&
499-
canEndStatTokens.contains(lastToken) &&
500-
canStartStatTokens.contains(token) &&
501-
!isLeadingInfixOperator())
498+
if newlineIsSeparating
499+
&& canEndStatTokens.contains(lastToken)
500+
&& canStartStatTokens.contains(token)
501+
&& !isLeadingInfixOperator()
502+
then
502503
insert(if (pastBlankLine) NEWLINES else NEWLINE, lineOffset)
503504
else if indentIsSignificant then
504505
if nextWidth < lastWidth
@@ -541,29 +542,19 @@ object Scanners {
541542
|Previous indent : $lastWidth
542543
|Latest indent : $nextWidth"""
543544

544-
def observeIndented(
545-
unless: BitSet = BitSet.empty,
546-
unlessSoftKW: TermName = EmptyTermName): Unit
547-
=
548-
if (indentSyntax && isAfterLineEnd && token != INDENT) {
549-
val nextOffset = if (isNewLine) next.offset else offset
550-
val nextToken = if (isNewLine) next.token else token
551-
val nextWidth = indentWidth(nextOffset)
552-
val lastWidth = currentRegion match {
545+
def observeIndented(): Unit =
546+
if indentSyntax && isNewLine then
547+
val nextWidth = indentWidth(next.offset)
548+
val lastWidth = currentRegion match
553549
case r: Indented => r.width
554550
case r: InBraces => r.width
555551
case _ => nextWidth
556-
}
557552

558-
if (lastWidth < nextWidth
559-
&& !unless.contains(nextToken)
560-
&& (unlessSoftKW.isEmpty || token != IDENTIFIER || name != unlessSoftKW)) {
553+
if lastWidth < nextWidth then
561554
currentRegion = Indented(nextWidth, Set(), COLONEOL, currentRegion)
562-
if (!isNewLine) next.copyFrom(this)
563-
offset = nextOffset
555+
offset = next.offset
564556
token = INDENT
565-
}
566-
}
557+
end observeIndented
567558

568559
/** - Join CASE + CLASS => CASECLASS, CASE + OBJECT => CASEOBJECT, SEMI + ELSE => ELSE, COLON + <EOL> => COLONEOL
569560
* - Insert missing OUTDENTs at EOF
@@ -1011,6 +1002,12 @@ object Scanners {
10111002
def isNestedStart = token == LBRACE || token == INDENT
10121003
def isNestedEnd = token == RBRACE || token == OUTDENT
10131004

1005+
def canStartStatTokens =
1006+
if isScala2Mode then canStartStatTokens2 else canStartStatTokens3
1007+
1008+
def canStartExprTokens =
1009+
if isScala2Mode then canStartExprTokens2 else canStartExprTokens3
1010+
10141011
// Literals -----------------------------------------------------------------
10151012

10161013
private def getStringLit() = {

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,10 @@ object Tokens extends TokensCommon {
219219
final val atomicExprTokens: TokenSet = literalTokens | identifierTokens | BitSet(
220220
USCORE, NULL, THIS, SUPER, TRUE, FALSE, RETURN, QUOTEID, XMLSTART)
221221

222-
final val canStartExpressionTokens: TokenSet = atomicExprTokens | BitSet(
223-
LBRACE, LPAREN, INDENT, QUOTE, IF, DO, WHILE, FOR, NEW, TRY, THROW)
222+
final val canStartExprTokens3: TokenSet = atomicExprTokens | BitSet(
223+
LBRACE, LPAREN, INDENT, QUOTE, IF, WHILE, FOR, NEW, TRY, THROW)
224+
225+
final val canStartExprTokens2: TokenSet = canStartExprTokens3 | BitSet(DO)
224226

225227
final val canStartTypeTokens: TokenSet = literalTokens | identifierTokens | BitSet(
226228
THIS, SUPER, USCORE, LPAREN, AT)
@@ -247,7 +249,9 @@ object Tokens extends TokensCommon {
247249
/** Is token only legal as start of statement (eof also included)? */
248250
final val mustStartStatTokens: TokenSet = defIntroTokens | modifierTokens | BitSet(IMPORT, EXPORT, PACKAGE)
249251

250-
final val canStartStatTokens: TokenSet = canStartExpressionTokens | mustStartStatTokens | BitSet(
252+
final val canStartStatTokens2: TokenSet = canStartExprTokens2 | mustStartStatTokens | BitSet(
253+
AT, CASE)
254+
final val canStartStatTokens3: TokenSet = canStartExprTokens3 | mustStartStatTokens | BitSet(
251255
AT, CASE)
252256

253257
final val canEndStatTokens: TokenSet = atomicExprTokens | BitSet(
@@ -280,10 +284,6 @@ object Tokens extends TokensCommon {
280284
*/
281285
final val startParamOrGivenTypeTokens: BitSet = startParamTokens | BitSet(GIVEN, ERASED)
282286

283-
final val noIndentTemplateTokens = BitSet(EXTENDS)
284-
final val noIndentAfterConditionTokens = BitSet(THEN, DO)
285-
final val noIndentAfterEnumeratorTokens = BitSet(YIELD, DO)
286-
287287
final val scala3keywords = BitSet(ENUM, ERASED, GIVEN)
288288

289289
final val softModifierNames = Set(nme.inline, nme.opaque)

compiler/src/dotty/tools/dotc/util/kwords.sc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ object kwords {
1313
//| atch, lazy, then, forSome, _, :, =, <-, =>, ';', ';', <:, >:, #, @, <%)
1414
keywords.toList.filter(kw => tokenString(kw) == null)
1515
//> res1: List[Int] = List()
16-
canStartStatTokens contains CASE //> res2: Boolean = false
17-
16+
canStartStatTokens3 contains CASE //> res2: Boolean = false
17+
1818
}

tests/pos/indent4.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,14 @@ object testindent
2222
val y = x
2323
println(y)
2424

25+
while true
26+
do println(1)
27+
28+
for i <- List(1, 2, 3)
29+
do println(i)
30+
31+
while (true)
32+
do println(1)
33+
34+
for (i <- List(1, 2, 3))
35+
do println(i)

0 commit comments

Comments
 (0)