Skip to content

Commit bd9744d

Browse files
authored
Merge pull request #7276 from dotty-staging/change-then-opt
Make `then` optional at line end
2 parents 19b4216 + 4d470dd commit bd9744d

File tree

16 files changed

+552
-176
lines changed

16 files changed

+552
-176
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
220220
if (sym == NothingClass) RT_NOTHING
221221
else if (sym == NullClass) RT_NULL
222222
else {
223-
val r = classBTypeFromSymbol(sym)
223+
val r = classBTypeFromSymbol(sym)
224224
if (r.isNestedClass) innerClassBufferASM += r
225225
r
226226
}

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

Lines changed: 79 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ object Parsers {
6969
* if not, the AST will be supplemented.
7070
*/
7171
def parser(source: SourceFile)(implicit ctx: Context): Parser =
72-
if (source.isSelfContained) new ScriptParser(source)
72+
if source.isSelfContained then new ScriptParser(source)
7373
else new Parser(source)
7474

7575
abstract class ParserCommon(val source: SourceFile)(implicit ctx: Context) {
@@ -181,7 +181,7 @@ object Parsers {
181181

182182
/* -------------- TOKEN CLASSES ------------------------------------------- */
183183

184-
def isIdent = in.token == IDENTIFIER || in.token == BACKQUOTED_IDENT
184+
def isIdent = in.isIdent
185185
def isIdent(name: Name) = in.token == IDENTIFIER && in.name == name
186186
def isSimpleLiteral = simpleLiteralTokens contains in.token
187187
def isLiteral = literalTokens contains in.token
@@ -207,16 +207,15 @@ 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 ||
214214
(defIntroTokens `contains` in.token) ||
215215
(allowedMods `contains` in.token) ||
216216
in.isSoftModifierInModifierPosition && !excludedSoftModifiers.contains(in.name)
217217

218-
def isStatSep: Boolean =
219-
in.token == NEWLINE || in.token == NEWLINES || in.token == SEMI
218+
def isStatSep: Boolean = in.isNewLine || in.token == SEMI
220219

221220
/** A '$' identifier is treated as a splice if followed by a `{`.
222221
* A longer identifier starting with `$` is treated as a splice/id combination
@@ -341,10 +340,8 @@ object Parsers {
341340
/** semi = nl {nl} | `;'
342341
* nl = `\n' // where allowed
343342
*/
344-
def acceptStatSep(): Unit = in.token match {
345-
case NEWLINE | NEWLINES => in.nextToken()
346-
case _ => accept(SEMI)
347-
}
343+
def acceptStatSep(): Unit =
344+
if in.isNewLine then in.nextToken() else accept(SEMI)
348345

349346
def acceptStatSepUnlessAtEnd(altEnd: Token = EOF): Unit =
350347
if (!isStatSeqEnd)
@@ -354,7 +351,7 @@ object Parsers {
354351
case NEWLINE | NEWLINES => in.nextToken()
355352
case SEMI => in.nextToken()
356353
case _ =>
357-
syntaxError(i"end of statement expected but $in found")
354+
syntaxError(i"end of statement expected but ${showToken(in.token)} found")
358355
in.nextToken() // needed to ensure progress; otherwise we might cycle forever
359356
accept(SEMI)
360357
}
@@ -603,9 +600,7 @@ object Parsers {
603600
val t = body()
604601
// Therefore, make sure there would be a matching <outdent>
605602
def nextIndentWidth = in.indentWidth(in.next.offset)
606-
if (in.token == NEWLINE || in.token == NEWLINES)
607-
&& !(nextIndentWidth < startIndentWidth)
608-
then
603+
if in.isNewLine && !(nextIndentWidth < startIndentWidth) then
609604
warning(
610605
if startIndentWidth <= nextIndentWidth then
611606
i"""Line is indented too far to the right, or a `{' is missing before:
@@ -623,7 +618,7 @@ object Parsers {
623618
* statement that's indented relative to the current region.
624619
*/
625620
def checkNextNotIndented(): Unit = in.currentRegion match
626-
case r: InBraces if in.token == NEWLINE || in.token == NEWLINES =>
621+
case r: IndentSignificantRegion if in.isNewLine =>
627622
val nextIndentWidth = in.indentWidth(in.next.offset)
628623
if r.indentWidth < nextIndentWidth then
629624
warning(i"Line is indented too far to the right, or a `{' is missing", in.next.offset)
@@ -813,20 +808,20 @@ object Parsers {
813808
else span
814809
}
815810

816-
/** Drop current token, which is assumed to be `then` or `do`. */
817-
def dropTerminator(): Unit = {
818-
var startOffset = in.offset
819-
var endOffset = in.lastCharOffset
820-
if (in.isAfterLineEnd) {
821-
if (testChar(endOffset, ' '))
822-
endOffset += 1
823-
}
824-
else
825-
if (testChar(startOffset - 1, ' ') &&
826-
!overlapsPatch(source, Span(startOffset - 1, endOffset)))
827-
startOffset -= 1
828-
patch(source, widenIfWholeLine(Span(startOffset, endOffset)), "")
829-
}
811+
/** Drop current token, if it is a `then` or `do`. */
812+
def dropTerminator(): Unit =
813+
if in.token == THEN || in.token == DO then
814+
var startOffset = in.offset
815+
var endOffset = in.lastCharOffset
816+
if (in.isAfterLineEnd) {
817+
if (testChar(endOffset, ' '))
818+
endOffset += 1
819+
}
820+
else
821+
if (testChar(startOffset - 1, ' ') &&
822+
!overlapsPatch(source, Span(startOffset - 1, endOffset)))
823+
startOffset -= 1
824+
patch(source, widenIfWholeLine(Span(startOffset, endOffset)), "")
830825

831826
/** rewrite code with (...) around the source code of `t` */
832827
def revertToParens(t: Tree): Unit =
@@ -841,7 +836,8 @@ object Parsers {
841836
/** In the tokens following the current one, does `query` precede any of the tokens that
842837
* - must start a statement, or
843838
* - separate two statements, or
844-
* - continue a statement (e.g. `else`, catch`)?
839+
* - continue a statement (e.g. `else`, catch`), or
840+
* - terminate the current scope?
845841
*/
846842
def followedByToken(query: Token): Boolean = {
847843
val lookahead = in.LookaheadScanner()
@@ -875,7 +871,7 @@ object Parsers {
875871
}
876872
if (lookahead.token == LARROW)
877873
false // it's a pattern
878-
else if (lookahead.token != IDENTIFIER && lookahead.token != BACKQUOTED_IDENT)
874+
else if (lookahead.isIdent)
879875
true // it's not a pattern since token cannot be an infix operator
880876
else
881877
followedByToken(LARROW) // `<-` comes before possible statement starts
@@ -903,7 +899,7 @@ object Parsers {
903899
*/
904900
def followingIsGivenSig() =
905901
val lookahead = in.LookaheadScanner()
906-
if lookahead.token == IDENTIFIER || lookahead.token == BACKQUOTED_IDENT then
902+
if lookahead.isIdent then
907903
lookahead.nextToken()
908904
while lookahead.token == LPAREN || lookahead.token == LBRACKET do
909905
lookahead.skipParens()
@@ -1229,13 +1225,16 @@ object Parsers {
12291225
if (in.token == NEWLINE) in.nextToken()
12301226

12311227
def newLinesOpt(): Unit =
1232-
if (in.token == NEWLINE || in.token == NEWLINES)
1233-
in.nextToken()
1228+
if in.isNewLine then in.nextToken()
12341229

12351230
def newLineOptWhenFollowedBy(token: Int): Unit =
12361231
// note: next is defined here because current == NEWLINE
12371232
if (in.token == NEWLINE && in.next.token == token) in.nextToken()
12381233

1234+
def newLinesOptWhenFollowedBy(name: Name): Unit =
1235+
if in.isNewLine && in.next.token == IDENTIFIER && in.next.name == name then
1236+
in.nextToken()
1237+
12391238
def newLineOptWhenFollowing(p: Int => Boolean): Unit =
12401239
// note: next is defined here because current == NEWLINE
12411240
if (in.token == NEWLINE && p(in.next.token)) newLineOpt()
@@ -1251,7 +1250,7 @@ object Parsers {
12511250
}
12521251

12531252
def possibleTemplateStart(): Unit = {
1254-
in.observeIndented(noIndentTemplateTokens, nme.derives)
1253+
in.observeIndented()
12551254
newLineOptWhenFollowedBy(LBRACE)
12561255
}
12571256

@@ -1650,35 +1649,51 @@ object Parsers {
16501649

16511650
/* ----------- EXPRESSIONS ------------------------------------------------ */
16521651

1652+
/** Does the current conditional expression continue after
1653+
* the initially parsed (...) region?
1654+
*/
1655+
def toBeContinued(altToken: Token): Boolean =
1656+
if in.token == altToken || in.isNewLine || in.isScala2Mode then
1657+
false // a newline token means the expression is finished
1658+
else if !in.canStartStatTokens.contains(in.token)
1659+
|| in.isLeadingInfixOperator(inConditional = true)
1660+
then
1661+
true
1662+
else
1663+
followedByToken(altToken) // scan ahead to see whether we find a `then` or `do`
1664+
16531665
def condExpr(altToken: Token): Tree =
1654-
if (in.token == LPAREN) {
1666+
if in.token == LPAREN then
16551667
var t: Tree = atSpan(in.offset) { Parens(inParens(exprInParens())) }
1656-
if (in.token != altToken && followedByToken(altToken))
1657-
t = inSepRegion(LPAREN, RPAREN) {
1658-
newLineOpt()
1668+
val enclosedInParens = !toBeContinued(altToken)
1669+
if !enclosedInParens then
1670+
t = inSepRegion(LBRACE, RBRACE) {
16591671
expr1Rest(postfixExprRest(simpleExprRest(t)), Location.ElseWhere)
16601672
}
1661-
if (in.token == altToken) {
1662-
if (rewriteToOldSyntax()) revertToParens(t)
1673+
if in.token == altToken then
1674+
if rewriteToOldSyntax() then revertToParens(t)
16631675
in.nextToken()
1664-
}
1665-
else {
1666-
in.observeIndented(noIndentAfterConditionTokens)
1676+
else
1677+
if (altToken == THEN || enclosedInParens) && in.isNewLine then
1678+
in.observeIndented()
1679+
if !enclosedInParens && in.token != INDENT then accept(altToken)
16671680
if (rewriteToNewSyntax(t.span))
16681681
dropParensOrBraces(t.span.start, s"${tokenString(altToken)}")
1669-
}
16701682
t
1671-
}
1672-
else {
1683+
else
16731684
val t =
1674-
if (in.isNestedStart)
1685+
if in.isNestedStart then
16751686
try expr() finally newLinesOpt()
16761687
else
1677-
inSepRegion(LPAREN, RPAREN)(expr())
1678-
if (rewriteToOldSyntax(t.span.startPos)) revertToParens(t)
1679-
accept(altToken)
1688+
inSepRegion(LBRACE, RBRACE)(expr())
1689+
if rewriteToOldSyntax(t.span.startPos) then
1690+
revertToParens(t)
1691+
if altToken == THEN && in.isNewLine then
1692+
// don't require a `then` at the end of a line
1693+
in.observeIndented()
1694+
if in.token != INDENT then accept(altToken)
16801695
t
1681-
}
1696+
end condExpr
16821697

16831698
/** Expr ::= [`implicit'] FunParams =>' Expr
16841699
* | Expr1
@@ -1841,20 +1856,20 @@ object Parsers {
18411856
}
18421857
}
18431858
case _ =>
1844-
if (isIdent(nme.inline) && !in.inModifierPosition() && in.lookaheadIn(canStartExpressionTokens)) {
1859+
if isIdent(nme.inline)
1860+
&& !in.inModifierPosition()
1861+
&& in.lookaheadIn(in.canStartExprTokens)
1862+
then
18451863
val start = in.skipToken()
1846-
in.token match {
1864+
in.token match
18471865
case IF =>
18481866
ifExpr(start, InlineIf)
18491867
case _ =>
18501868
val t = postfixExpr()
18511869
if (in.token == MATCH) matchExpr(t, start, InlineMatch)
1852-
else {
1870+
else
18531871
syntaxErrorOrIncomplete(i"`match` or `if` expected but ${in.token} found")
18541872
t
1855-
}
1856-
}
1857-
}
18581873
else expr1Rest(postfixExpr(), location)
18591874
}
18601875

@@ -2001,7 +2016,7 @@ object Parsers {
20012016
def postfixExpr(): Tree = postfixExprRest(prefixExpr())
20022017

20032018
def postfixExprRest(t: Tree): Tree =
2004-
infixOps(t, canStartExpressionTokens, prefixExpr, maybePostfix = true)
2019+
infixOps(t, in.canStartExprTokens, prefixExpr, maybePostfix = true)
20052020

20062021
/** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr
20072022
*/
@@ -2198,7 +2213,7 @@ object Parsers {
21982213
lookahead.nextToken()
21992214
lookahead.token != COLON
22002215
}
2201-
else canStartExpressionTokens.contains(lookahead.token)
2216+
else in.canStartExprTokens.contains(lookahead.token)
22022217
}
22032218
}
22042219
if (in.token == LPAREN && (!inClassConstrAnnots || isLegalAnnotArg))
@@ -2327,7 +2342,7 @@ object Parsers {
23272342
dropParensOrBraces(start, if (in.token == YIELD || in.token == DO) "" else "do")
23282343
}
23292344
}
2330-
in.observeIndented(noIndentAfterEnumeratorTokens)
2345+
in.observeIndented()
23312346
res
23322347
}
23332348
else {
@@ -2473,7 +2488,7 @@ object Parsers {
24732488
/** InfixPattern ::= SimplePattern {id [nl] SimplePattern}
24742489
*/
24752490
def infixPattern(): Tree =
2476-
infixOps(simplePattern(), canStartExpressionTokens, simplePattern, isOperator = in.name != nme.raw.BAR)
2491+
infixOps(simplePattern(), in.canStartExprTokens, simplePattern, isOperator = in.name != nme.raw.BAR)
24772492

24782493
/** SimplePattern ::= PatVar
24792494
* | Literal
@@ -3459,6 +3474,7 @@ object Parsers {
34593474
/** Template ::= InheritClauses [TemplateBody]
34603475
*/
34613476
def template(constr: DefDef, isEnum: Boolean = false): Template = {
3477+
newLinesOptWhenFollowedBy(nme.derives)
34623478
val (parents, derived) = inheritClauses()
34633479
possibleTemplateStart()
34643480
if (isEnum) {
@@ -3471,10 +3487,11 @@ object Parsers {
34713487
/** TemplateOpt = [Template]
34723488
*/
34733489
def templateOpt(constr: DefDef): Template =
3474-
possibleTemplateStart()
3490+
newLinesOptWhenFollowedBy(nme.derives)
34753491
if in.token == EXTENDS || isIdent(nme.derives) then
34763492
template(constr)
34773493
else
3494+
possibleTemplateStart()
34783495
if in.isNestedStart then
34793496
template(constr)
34803497
else

0 commit comments

Comments
 (0)