@@ -226,8 +226,8 @@ object Scanners {
226226 /** The current region. This is initially an Indented region with indentation width. */
227227 var currentRegion : Region = Indented (IndentWidth .Zero , Set (), EMPTY , null )
228228
229- /** The end marker that was skipped last */
230- val endMarkers = new mutable. ListBuffer [ EndMarker ]
229+ /** The number of open end marker scopes */
230+ var openEndMarkers : List [( EndMarkerTag , IndentWidth )] = Nil
231231
232232// Scala 2 compatibility
233233
@@ -324,48 +324,47 @@ object Scanners {
324324 this .token = token
325325 }
326326
327- /** If this token and the next constitute an end marker, skip them and append a new EndMarker
328- * value at the end of the endMarkers queue.
327+ /** What can be referred to in an end marker */
328+ type EndMarkerTag = TermName | Token
329+
330+ /** Establish a scope for a passible end-marker with given tag, parsed by `op` */
331+ def endMarkerScope [T ](tag : EndMarkerTag )(op : => T ): T =
332+ val saved = openEndMarkers
333+ openEndMarkers = (tag, currentRegion.indentWidth) :: openEndMarkers
334+ try op finally openEndMarkers = saved
335+
336+ /** If this token and the next constitute an end marker, skip them and check they
337+ * align with an opening construct with the same end marker tag.
329338 */
330- private def handleEndMarkers (width : IndentWidth ): Unit =
331- if ( next.token == IDENTIFIER && next.name == nme.end && width == currentRegion.indentWidth) {
339+ protected def skipEndMarker (width : IndentWidth ): Unit =
340+ if next.token == IDENTIFIER && next.name == nme.end then
332341 val lookahead = LookaheadScanner ()
333- lookahead.nextToken() // skip the `end`
342+ val start = lookahead.offset
343+
344+ def handle (tag : EndMarkerTag ) =
345+ def checkAligned (): Unit = openEndMarkers match
346+ case (etag, ewidth) :: rest if width <= ewidth =>
347+ if width < ewidth || tag != etag then
348+ openEndMarkers = rest
349+ checkAligned()
350+ case _ =>
351+ lexical.println(i " misaligned end marker $tag, $width, $openEndMarkers" )
352+ errorButContinue(" misaligned end marker" , start)
334353
335- def handle (tag : EndMarkerTag ) = {
336354 val skipTo = lookahead.charOffset
337355 lookahead.nextToken()
338- if (lookahead.isAfterLineEnd || lookahead.token == EOF ) {
339- lexical.println(i " produce end marker $tag $width" )
340- endMarkers += EndMarker (tag, width, offset)
356+ if lookahead.isAfterLineEnd || lookahead.token == EOF then
357+ checkAligned()
341358 next.token = EMPTY
342- while (charOffset < skipTo) nextChar()
343- }
344- }
359+ while charOffset < skipTo do nextChar()
360+ end handle
345361
346- lookahead.token match {
362+ lookahead.nextToken() // skip the `end`
363+ lookahead.token match
347364 case IDENTIFIER | BACKQUOTED_IDENT => handle(lookahead.name)
348365 case IF | WHILE | FOR | MATCH | TRY | NEW | GIVEN => handle(lookahead.token)
349366 case _ =>
350- }
351- }
352-
353- /** Consume and cancel the head of the end markers queue if it has the given `tag` and width.
354- * Flag end markers with higher indent widths as errors.
355- */
356- def consumeEndMarker (tag : EndMarkerTag , width : IndentWidth ): Unit = {
357- lexical.println(i " consume end marker $tag $width" )
358- if (endMarkers.nonEmpty) {
359- val em = endMarkers.head
360- if (width <= em.width) {
361- if (em.tag != tag || em.width != width) {
362- lexical.println(i " misaligned end marker ${em.tag}, ${em.width} at ${width}" )
363- errorButContinue(" misaligned end marker" , em.offset)
364- }
365- endMarkers.trimStart(1 )
366- }
367- }
368- }
367+ end skipEndMarker
369368
370369 /** A leading symbolic or backquoted identifier is treated as an infix operator if
371370 * - it does not follow a blank line, and
@@ -501,6 +500,7 @@ object Scanners {
501500 && ! isLeadingInfixOperator()
502501 then
503502 insert(if (pastBlankLine) NEWLINES else NEWLINE , lineOffset)
503+ skipEndMarker(nextWidth)
504504 else if indentIsSignificant then
505505 if nextWidth < lastWidth
506506 || nextWidth == lastWidth && (indentPrefix == MATCH || indentPrefix == CATCH ) && token != CASE then
@@ -511,7 +511,7 @@ object Scanners {
511511 case r : Indented =>
512512 currentRegion = r.enclosing
513513 insert(OUTDENT , offset)
514- handleEndMarkers (nextWidth)
514+ skipEndMarker (nextWidth)
515515 case r : InBraces if ! closingRegionTokens.contains(token) =>
516516 ctx.warning(" Line is indented too far to the left, or a `}' is missing" ,
517517 source.atSpan(Span (offset)))
@@ -883,6 +883,7 @@ object Scanners {
883883
884884 class LookaheadScanner (indent : Boolean = false ) extends Scanner (source, offset) {
885885 override val indentSyntax = indent
886+ override def skipEndMarker (width : IndentWidth ) = ()
886887 override protected def printState () = {
887888 print(" la:" )
888889 super .printState()
@@ -1416,16 +1417,6 @@ object Scanners {
14161417 val Zero = Run (' ' , 0 )
14171418 }
14181419
1419- /** What can be referred to in an end marker */
1420- type EndMarkerTag = TermName | Token
1421-
1422- /** A processed end marker
1423- * @param tag The name or token referred to in the marker
1424- * @param width The indentation width where the marker occurred
1425- * @param offset The offset of the `end`
1426- */
1427- case class EndMarker (tag : EndMarkerTag , width : IndentWidth , offset : Int )
1428-
14291420 // ------------- keyword configuration -----------------------------------
14301421
14311422 private val (lastKeywordStart, kwArray) = buildKeywordArray(keywords)
0 commit comments