Skip to content

Commit d3670a6

Browse files
committed
Recognize fewer braces COLONeol's depending on previous token
- Drop the criterion of no whitespace. - Also, refactor the logic so that tokens are converted to COLONeol only on demand.
1 parent c1f18cb commit d3670a6

File tree

4 files changed

+61
-52
lines changed

4 files changed

+61
-52
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ object JavaScanners {
179179
nextChar()
180180

181181
case ':' =>
182-
token = COLON
182+
token = COLONop
183183
nextChar()
184184

185185
case '@' =>

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

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ object Parsers {
312312
def acceptColon(): Int =
313313
val offset = in.offset
314314
if in.isColon() then { in.nextToken(); offset }
315-
else accept(COLON)
315+
else accept(COLONop)
316316

317317
/** semi = nl {nl} | `;'
318318
* nl = `\n' // where allowed
@@ -897,10 +897,11 @@ object Parsers {
897897
def followingIsSelfType() =
898898
val lookahead = in.LookaheadScanner(allowIndent = true)
899899
lookahead.nextToken()
900-
lookahead.token == COLON
900+
lookahead.token == COLONfollow
901901
&& {
902+
lookahead.observeColonEOL(inTemplate = false)
902903
lookahead.nextToken()
903-
canStartTypeTokens(lookahead.token)
904+
canStartTypeTokens.contains(lookahead.token)
904905
}
905906

906907
/** Is current ident a `*`, and is it followed by a `)`, `, )`, `,EOF`? The latter two are not
@@ -1310,7 +1311,8 @@ object Parsers {
13101311

13111312
def colonAtEOLOpt(): Unit = {
13121313
possibleColonOffset = in.lastOffset
1313-
if in.token == COLONEOL then in.nextToken()
1314+
in.observeColonEOL(inTemplate = false)
1315+
if in.token == COLONeol then in.nextToken()
13141316
}
13151317

13161318
def argumentStart(): Unit =
@@ -1326,8 +1328,8 @@ object Parsers {
13261328
patch(source, Span(in.offset), " ")
13271329

13281330
def possibleTemplateStart(isNew: Boolean = false): Unit =
1329-
in.observeColonEOL()
1330-
if in.token == COLONEOL then
1331+
in.observeColonEOL(inTemplate = true)
1332+
if in.token == COLONeol then
13311333
if in.lookahead.token == END then in.token = NEWLINE
13321334
else
13331335
in.nextToken()
@@ -2127,7 +2129,7 @@ object Parsers {
21272129
}
21282130
case _ =>
21292131
t
2130-
case COLON =>
2132+
case COLONop | COLONfollow =>
21312133
in.nextToken()
21322134
ascription(t, location)
21332135
case _ =>
@@ -2216,7 +2218,7 @@ object Parsers {
22162218
val start = in.offset
22172219
val name = bindingName()
22182220
val t =
2219-
if (in.token == COLON && location == Location.InBlock) {
2221+
if ((in.token == COLONop || in.token == COLONfollow) && location == Location.InBlock) {
22202222
report.errorOrMigrationWarning(
22212223
s"This syntax is no longer supported; parameter needs to be enclosed in (...)${rewriteNotice(`future-migration`)}",
22222224
source.atSpan(Span(start, in.lastOffset)),
@@ -2398,7 +2400,7 @@ object Parsers {
23982400
makeParameter(name.asTermName, typedOpt(), Modifiers(), isBackquoted = isBackquoted(id))
23992401
}
24002402
case _ => t
2401-
else if in.fewerBracesEnabled && in.token == COLON && followingIsLambdaAfterColon() then
2403+
else if in.fewerBracesEnabled && in.token == COLONfollow && followingIsLambdaAfterColon() then
24022404
val app = atSpan(startOffset(t), in.skipToken()) {
24032405
Apply(t, expr(Location.InColonArg) :: Nil)
24042406
}
@@ -2416,7 +2418,6 @@ object Parsers {
24162418
val parents =
24172419
if in.isNestedStart then Nil
24182420
else constrApps(exclude = COMMA)
2419-
colonAtEOLOpt()
24202421
possibleTemplateStart(isNew = true)
24212422
parents match {
24222423
case parent :: Nil if !in.isNestedStart =>
@@ -2726,7 +2727,7 @@ object Parsers {
27262727
*/
27272728
def pattern1(location: Location = Location.InPattern): Tree =
27282729
val p = pattern2()
2729-
if in.token == COLON then
2730+
if in.token == COLONop || in.token == COLONfollow then
27302731
in.nextToken()
27312732
ascription(p, location)
27322733
else p
@@ -3836,7 +3837,7 @@ object Parsers {
38363837
val parents =
38373838
if (in.token == EXTENDS) {
38383839
in.nextToken()
3839-
if (in.token == LBRACE || in.token == COLONEOL) {
3840+
if (in.token == LBRACE || in.token == COLONeol) {
38403841
report.errorOrMigrationWarning(
38413842
"`extends` must be followed by at least one parent",
38423843
in.sourcePos(), from = `3.0`)
@@ -3957,8 +3958,8 @@ object Parsers {
39573958
*/
39583959
def selfType(): ValDef =
39593960
if (in.isIdent || in.token == THIS)
3960-
&& (in.lookahead.token == COLON && followingIsSelfType()
3961-
|| in.lookahead.token == ARROW)
3961+
&& in.lookahead.token == COLONop && followingIsSelfType()
3962+
|| in.lookahead.token == ARROW
39623963
then
39633964
atSpan(in.offset) {
39643965
val selfName =
@@ -3967,11 +3968,11 @@ object Parsers {
39673968
nme.WILDCARD
39683969
else ident()
39693970
val selfTpt =
3970-
if in.token == COLON then
3971+
if in.token == COLONfollow then
39713972
in.nextToken()
39723973
infixType()
39733974
else
3974-
if selfName == nme.WILDCARD then accept(COLON)
3975+
if selfName == nme.WILDCARD then accept(COLONfollow)
39753976
TypeTree()
39763977
if in.token == ARROW then
39773978
in.token = SELFARROW // suppresses INDENT insertion after `=>`

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

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,13 @@ object Scanners {
7575
def isNestedStart = token == LBRACE || token == INDENT
7676
def isNestedEnd = token == RBRACE || token == OUTDENT
7777

78-
/** Is token a COLON, after having converted COLONEOL to COLON?
78+
/** Is token a COLON, after having converted COLONeol to COLON?
7979
* The conversion means that indentation is not significant after `:`
8080
* anymore. So, warning: this is a side-effecting operation.
8181
*/
8282
def isColon() =
83-
if token == COLONEOL then token = COLON
84-
token == COLON
83+
if token == COLONeol then token = COLONop
84+
token == COLONop || token == COLONfollow
8585

8686
/** Is current token first one after a newline? */
8787
def isAfterLineEnd: Boolean = lineOffset >= 0
@@ -394,10 +394,11 @@ object Scanners {
394394
*/
395395
def nextToken(): Unit =
396396
val lastToken = token
397+
val lastName = name
397398
adjustSepRegions(lastToken)
398399
getNextToken(lastToken)
399400
if isAfterLineEnd then handleNewLine(lastToken)
400-
postProcessToken(lastToken)
401+
postProcessToken(lastToken, lastName)
401402
printState()
402403

403404
final def printState() =
@@ -428,7 +429,7 @@ object Scanners {
428429
&& {
429430
// Is current lexeme assumed to start an expression?
430431
// This is the case if the lexime is one of the tokens that
431-
// starts an expression or it is a COLONEOL. Furthermore, if
432+
// starts an expression or it is a COLONeol. Furthermore, if
432433
// the previous token is in backticks, the lexeme may not be a binary operator.
433434
// I.e. in
434435
//
@@ -439,7 +440,7 @@ object Scanners {
439440
// in backticks and is a binary operator. Hence, `x` is not classified as a
440441
// leading infix operator.
441442
def assumeStartsExpr(lexeme: TokenData) =
442-
(canStartExprTokens.contains(lexeme.token) || lexeme.token == COLONEOL)
443+
(canStartExprTokens.contains(lexeme.token) || lexeme.token == COLONeol)
443444
&& (!lexeme.isOperator || nme.raw.isUnary(lexeme.name))
444445
val lookahead = LookaheadScanner()
445446
lookahead.allowLeadingInfixOperators = false
@@ -615,12 +616,11 @@ object Scanners {
615616
currentRegion match
616617
case r: Indented =>
617618
insert(OUTDENT, offset)
618-
if next.token != COLON then
619-
handleNewIndentWidth(r.enclosing, ir =>
620-
errorButContinue(
621-
i"""The start of this line does not match any of the previous indentation widths.
622-
|Indentation width of current line : $nextWidth
623-
|This falls between previous widths: ${ir.width} and $lastWidth"""))
619+
handleNewIndentWidth(r.enclosing, ir =>
620+
errorButContinue(
621+
i"""The start of this line does not match any of the previous indentation widths.
622+
|Indentation width of current line : $nextWidth
623+
|This falls between previous widths: ${ir.width} and $lastWidth"""))
624624
case r =>
625625
if skipping then
626626
if r.enclosing.isClosedByUndentAt(nextWidth) then
@@ -637,7 +637,7 @@ object Scanners {
637637
currentRegion.knownWidth = nextWidth
638638
else if (lastWidth != nextWidth)
639639
errorButContinue(spaceTabMismatchMsg(lastWidth, nextWidth))
640-
if token != OUTDENT || next.token == COLON then
640+
if token != OUTDENT then
641641
handleNewIndentWidth(currentRegion, _.otherIndentWidths += nextWidth)
642642
end handleNewLine
643643

@@ -646,19 +646,22 @@ object Scanners {
646646
|Previous indent : $lastWidth
647647
|Latest indent : $nextWidth"""
648648

649-
def observeColonEOL(): Unit =
650-
if token == COLON then
649+
def observeColonEOL(inTemplate: Boolean): Unit =
650+
val enabled =
651+
if inTemplate then token == COLONop || token == COLONfollow
652+
else token == COLONfollow && fewerBracesEnabled
653+
if enabled then
651654
lookAhead()
652655
val atEOL = isAfterLineEnd || token == EOF
653656
reset()
654-
if atEOL then token = COLONEOL
657+
if atEOL then token = COLONeol
655658

656659
def observeIndented(): Unit =
657660
if indentSyntax && isNewLine then
658661
val nextWidth = indentWidth(next.offset)
659662
val lastWidth = currentRegion.indentWidth
660663
if lastWidth < nextWidth then
661-
currentRegion = Indented(nextWidth, COLONEOL, currentRegion)
664+
currentRegion = Indented(nextWidth, COLONeol, currentRegion)
662665
offset = next.offset
663666
token = INDENT
664667
end observeIndented
@@ -691,10 +694,10 @@ object Scanners {
691694
case _ =>
692695

693696
/** - Join CASE + CLASS => CASECLASS, CASE + OBJECT => CASEOBJECT
694-
* SEMI + ELSE => ELSE, COLON + <EOL> => COLONEOL
697+
* SEMI + ELSE => ELSE, COLON following id/)/] => COLONfollow
695698
* - Insert missing OUTDENTs at EOF
696699
*/
697-
def postProcessToken(lastToken: Token): Unit = {
700+
def postProcessToken(lastToken: Token, lastName: SimpleName): Unit = {
698701
def fuse(tok: Int) = {
699702
token = tok
700703
offset = prev.offset
@@ -729,9 +732,10 @@ object Scanners {
729732
reset()
730733
case END =>
731734
if !isEndMarker then token = IDENTIFIER
732-
case COLON =>
733-
if fewerBracesEnabled && colonEOLPredecessors.contains(lastToken) && lastOffset == offset then
734-
observeColonEOL()
735+
case COLONop =>
736+
if lastToken == IDENTIFIER && lastName != null && isIdentifierStart(lastName.head)
737+
|| colonEOLPredecessors.contains(lastToken)
738+
then token = COLONfollow
735739
case RBRACE | RPAREN | RBRACKET =>
736740
closeIndented()
737741
case EOF =>
@@ -1188,7 +1192,7 @@ object Scanners {
11881192
isSoftModifier && inModifierPosition()
11891193

11901194
def isSoftModifierInParamModifierPosition: Boolean =
1191-
isSoftModifier && lookahead.token != COLON
1195+
isSoftModifier && lookahead.token != COLONop && lookahead.token != COLONfollow
11921196

11931197
def isErased: Boolean = isIdent(nme.erased) && erasedEnabled
11941198

@@ -1527,6 +1531,7 @@ object Scanners {
15271531
case NEWLINE => ";"
15281532
case NEWLINES => ";;"
15291533
case COMMA => ","
1534+
case COLONfollow | COLONeol => "':'"
15301535
case _ =>
15311536
if debugTokenStream then showTokenDetailed(token) else showToken(token)
15321537
}

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

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,6 @@ abstract class TokensCommon {
1616

1717
def tokenRange(lo: Int, hi: Int): TokenSet = BitSet(lo to hi: _*)
1818

19-
def showTokenDetailed(token: Int): String = debugString(token)
20-
21-
def showToken(token: Int): String = {
22-
val str = tokenString(token)
23-
if (isKeyword(token)) s"'$str'" else str
24-
}
25-
2619
val tokenString, debugString: Array[String] = new Array[String](maxToken + 1)
2720

2821
def enter(token: Int, str: String, debugStr: String = ""): Unit = {
@@ -107,7 +100,7 @@ abstract class TokensCommon {
107100

108101
/** special keywords */
109102
//inline val USCORE = 73; enter(USCORE, "_")
110-
inline val COLON = 74; enter(COLON, ":")
103+
inline val COLONop = 74; enter(COLONop, ":") // a stand-alone `:`, see also COLONfollow
111104
inline val EQUALS = 75; enter(EQUALS, "=")
112105
//inline val LARROW = 76; enter(LARROW, "<-")
113106
//inline val ARROW = 77; enter(ARROW, "=>")
@@ -204,8 +197,11 @@ object Tokens extends TokensCommon {
204197

205198
inline val QUOTE = 87; enter(QUOTE, "'")
206199

207-
inline val COLONEOL = 88; enter(COLONEOL, ":", ": at eol")
208-
inline val SELFARROW = 89; enter(SELFARROW, "=>") // reclassified ARROW following self-type
200+
inline val COLONfollow = 88; enter(COLONfollow, ":")
201+
// A `:` following an alphanumeric identifier or one of the tokens in colonEOLPredecessors
202+
inline val COLONeol = 89; enter(COLONeol, ":", ": at eol")
203+
// A `:` recognized as starting an indentation block
204+
inline val SELFARROW = 90; enter(SELFARROW, "=>") // reclassified ARROW following self-type
209205

210206
/** XML mode */
211207
inline val XMLSTART = 99; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
@@ -276,7 +272,7 @@ object Tokens extends TokensCommon {
276272
final val closingRegionTokens = BitSet(RBRACE, RPAREN, RBRACKET, CASE) | statCtdTokens
277273

278274
final val canStartIndentTokens: BitSet =
279-
statCtdTokens | BitSet(COLONEOL, WITH, EQUALS, ARROW, CTXARROW, LARROW, WHILE, TRY, FOR, IF, THROW, RETURN)
275+
statCtdTokens | BitSet(COLONeol, WITH, EQUALS, ARROW, CTXARROW, LARROW, WHILE, TRY, FOR, IF, THROW, RETURN)
280276

281277
/** Faced with the choice between a type and a formal parameter, the following
282278
* tokens determine it's a formal parameter.
@@ -287,9 +283,16 @@ object Tokens extends TokensCommon {
287283

288284
final val endMarkerTokens = identifierTokens | BitSet(IF, WHILE, FOR, MATCH, TRY, NEW, THROW, GIVEN, VAL, THIS)
289285

290-
final val colonEOLPredecessors = identifierTokens | BitSet(RPAREN, RBRACKET)
286+
final val colonEOLPredecessors = BitSet(RPAREN, RBRACKET, BACKQUOTED_IDENT, THIS, SUPER, QUOTEID, STRINGLIT, NEW)
291287

292288
final val closingParens = BitSet(RPAREN, RBRACKET, RBRACE)
293289

294290
final val softModifierNames = Set(nme.inline, nme.opaque, nme.open, nme.transparent, nme.infix)
291+
292+
def showTokenDetailed(token: Int): String = debugString(token)
293+
294+
def showToken(token: Int): String = {
295+
val str = tokenString(token)
296+
if isKeyword(token) || token == COLONfollow || token == COLONeol then s"'$str'" else str
297+
}
295298
}

0 commit comments

Comments
 (0)