Skip to content

Commit 48ad814

Browse files
committed
OUTDENT refactoring
Drop Indented region only after seeing an OUTDENT node. This aligns region stack popping with the other closing tokens `]`, `)`, and `}` and thereby allows to simplify `skip`.
1 parent 8506bde commit 48ad814

File tree

4 files changed

+41
-38
lines changed

4 files changed

+41
-38
lines changed

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

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -254,22 +254,16 @@ object Parsers {
254254
*/
255255
protected def skip(): Unit =
256256
val lastRegion = in.currentRegion
257-
var prevRegion = lastRegion
258-
// the region before the last token
259-
// we need to keep track of this since OUTDENT immediately pops
260-
// the region whereas other closing tokens do that only when the
261-
// next token is encountered.
262257
in.skipping = true
263258
def atStop =
264259
in.token == EOF
265-
|| skipStopTokens.contains(in.token) && (in.currentRegion eq lastRegion)
266-
|| in.token == OUTDENT && (prevRegion eq lastRegion) && in.indentWidth(in.offset) < in.lastKnownIndentWidth
267-
// stop at OUTDENT if (1) currentRegion is the one that was just closed by the OUTDENT
268-
// and (2) the new indentwidth is smaller than the indent width of currentRegion.
269-
// The second condition corrects for the problem that sometimes we don't see an INDENT
260+
|| (in.currentRegion eq lastRegion)
261+
&& (skipStopTokens.contains(in.token)
262+
|| in.token == OUTDENT && in.indentWidth(in.offset) < in.lastKnownIndentWidth)
263+
// stop at OUTDENT if the new indentwidth is smaller than the indent width of
264+
// currentRegion. This corrects for the problem that sometimes we don't see an INDENT
270265
// when skipping and therefore might erroneously end up syncing on a nested OUTDENT.
271266
while !atStop do
272-
prevRegion = in.currentRegion
273267
in.nextToken()
274268
in.recoverSepRegions()
275269
lastErrorOffset = in.offset

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

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ object Scanners {
274274
private val prev = newTokenData
275275

276276
/** The current region. This is initially an Indented region with zero indentation width. */
277-
var currentRegion: Region = Indented(IndentWidth.Zero, Set(), EMPTY, null)
277+
var currentRegion: Region = Indented(IndentWidth.Zero, EMPTY, null)
278278

279279
def lastKnownIndentWidth: IndentWidth =
280280
def recur(r: Region | Null): IndentWidth =
@@ -333,6 +333,10 @@ object Scanners {
333333
case InParens(prefix, outer) if prefix + 1 == lastToken => currentRegion = outer
334334
case _ =>
335335
}
336+
case OUTDENT =>
337+
currentRegion match
338+
case r: Indented => currentRegion = r.enclosing
339+
case _ =>
336340
case STRINGLIT =>
337341
currentRegion match {
338342
case InString(_, outer) => currentRegion = outer
@@ -441,8 +445,8 @@ object Scanners {
441445
|| {
442446
r.outer match
443447
case null => true
444-
case Indented(outerWidth, others, _, _) =>
445-
outerWidth < nextWidth && !others.contains(nextWidth)
448+
case ro @ Indented(outerWidth, _, _) =>
449+
outerWidth < nextWidth && !ro.otherIndentWidths.contains(nextWidth)
446450
case outer =>
447451
outer.indentWidth < nextWidth
448452
}
@@ -548,6 +552,15 @@ object Scanners {
548552
var lastWidth = IndentWidth.Zero
549553
var indentPrefix = EMPTY
550554
val nextWidth = indentWidth(offset)
555+
556+
// If nextWidth is an indentation level not yet seen by enclosing indentation
557+
// region, invoke `handler`.
558+
def handleNewIndentWidth(r: Region, handler: Indented => Unit): Unit = r match
559+
case r @ Indented(curWidth, prefix, outer)
560+
if curWidth < nextWidth && !r.otherIndentWidths.contains(nextWidth) && nextWidth != lastWidth =>
561+
handler(r)
562+
case _ =>
563+
551564
currentRegion match
552565
case r: Indented =>
553566
indentIsSignificant = indentSyntax
@@ -576,32 +589,28 @@ object Scanners {
576589
else if !isLeadingInfixOperator(nextWidth) && !statCtdTokens.contains(lastToken) && lastToken != INDENT then
577590
currentRegion match
578591
case r: Indented =>
579-
currentRegion = r.enclosing
580592
insert(OUTDENT, offset)
593+
if next.token != COLON then
594+
handleNewIndentWidth(r.enclosing, ir =>
595+
errorButContinue(
596+
i"""The start of this line does not match any of the previous indentation widths.
597+
|Indentation width of current line : $nextWidth
598+
|This falls between previous widths: ${ir.width} and $lastWidth"""))
581599
case r: InBraces if !closingRegionTokens.contains(token) =>
582600
report.warning("Line is indented too far to the left, or a `}` is missing", sourcePos())
583601
case _ =>
584602

585603
else if lastWidth < nextWidth
586604
|| lastWidth == nextWidth && (lastToken == MATCH || lastToken == CATCH) && token == CASE then
587605
if canStartIndentTokens.contains(lastToken) then
588-
currentRegion = Indented(nextWidth, Set(), lastToken, currentRegion)
606+
currentRegion = Indented(nextWidth, lastToken, currentRegion)
589607
insert(INDENT, offset)
590608
else if lastToken == SELFARROW then
591609
currentRegion.knownWidth = nextWidth
592610
else if (lastWidth != nextWidth)
593611
errorButContinue(spaceTabMismatchMsg(lastWidth, nextWidth))
594-
currentRegion match
595-
case Indented(curWidth, others, prefix, outer)
596-
if curWidth < nextWidth && !others.contains(nextWidth) && nextWidth != lastWidth =>
597-
if token == OUTDENT && next.token != COLON then
598-
errorButContinue(
599-
i"""The start of this line does not match any of the previous indentation widths.
600-
|Indentation width of current line : $nextWidth
601-
|This falls between previous widths: $curWidth and $lastWidth""")
602-
else
603-
currentRegion = Indented(curWidth, others + nextWidth, prefix, outer)
604-
case _ =>
612+
if token != OUTDENT || next.token == COLON then
613+
handleNewIndentWidth(currentRegion, _.otherIndentWidths += nextWidth)
605614
end handleNewLine
606615

607616
def spaceTabMismatchMsg(lastWidth: IndentWidth, nextWidth: IndentWidth) =
@@ -621,7 +630,7 @@ object Scanners {
621630
val nextWidth = indentWidth(next.offset)
622631
val lastWidth = currentRegion.indentWidth
623632
if lastWidth < nextWidth then
624-
currentRegion = Indented(nextWidth, Set(), COLONEOL, currentRegion)
633+
currentRegion = Indented(nextWidth, COLONEOL, currentRegion)
625634
offset = next.offset
626635
token = INDENT
627636
end observeIndented
@@ -636,7 +645,6 @@ object Scanners {
636645
&& !(token == CASE && r.prefix == MATCH)
637646
&& next.token == EMPTY // can be violated for ill-formed programs, e.g. neg/i12605.sala
638647
=>
639-
currentRegion = r.enclosing
640648
insert(OUTDENT, offset)
641649
case _ =>
642650

@@ -651,9 +659,7 @@ object Scanners {
651659
}
652660

653661
def closeIndented() = currentRegion match
654-
case r: Indented if !r.isOutermost =>
655-
insert(OUTDENT, offset)
656-
currentRegion = r.outer
662+
case r: Indented if !r.isOutermost => insert(OUTDENT, offset)
657663
case _ =>
658664

659665
/** - Join CASE + CLASS => CASECLASS, CASE + OBJECT => CASEOBJECT
@@ -684,7 +690,6 @@ object Scanners {
684690
currentRegion match
685691
case r: Indented if isEnclosedInParens(r.outer) =>
686692
insert(OUTDENT, offset)
687-
currentRegion = r.outer
688693
case _ =>
689694
lookAhead()
690695
if isAfterLineEnd
@@ -1570,13 +1575,16 @@ object Scanners {
15701575

15711576
/** A class describing an indentation region.
15721577
* @param width The principal indendation width
1573-
* @param others Other indendation widths > width of lines in the same region
15741578
* @param prefix The token before the initial <indent> of the region
15751579
*/
1576-
case class Indented(width: IndentWidth, others: Set[IndentWidth], prefix: Token, outer: Region | Null) extends Region:
1580+
case class Indented(width: IndentWidth, prefix: Token, outer: Region | Null) extends Region:
15771581
knownWidth = width
15781582

1579-
def topLevelRegion(width: IndentWidth) = Indented(width, Set(), EMPTY, null)
1583+
/** Other indendation widths > width of lines in the same region */
1584+
var otherIndentWidths: Set[IndentWidth] = Set()
1585+
end Indented
1586+
1587+
def topLevelRegion(width: IndentWidth) = Indented(width, EMPTY, null)
15801588

15811589
enum IndentWidth {
15821590
case Run(ch: Char, n: Int)

tests/neg/i12605.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
object Foo:
22
def joe(): List[(Int, Int)] =
33
List((2, 3), (3, 4)).filter case (a, b) => b > a // error // error
4+
// error

tests/neg/indent.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ object Test {
44
val y3 =
55
if (1) max 10 gt 0 // error: end of statement expected but integer literal found // error // error
66
1
7-
else
7+
else // error
88
2
9-
} // error
9+
}

0 commit comments

Comments
 (0)