@@ -585,6 +585,50 @@ object Parsers {
585
585
try op finally in.adjustSepRegions(closing)
586
586
}
587
587
588
+ /** Parse `body` while checking (under -noindent) that a `{` is not missing before it.
589
+ * This is done as follows:
590
+ * If the next token S is indented relative to the current region,
591
+ * and the end of `body` is followed by a new line and another statement,
592
+ * check that that other statement is indented less than S
593
+ */
594
+ def subPart [T ](body : () => T ): T = in.currentRegion match
595
+ case r : InBraces if in.isAfterLineEnd =>
596
+ val startIndentWidth = in.indentWidth(in.offset)
597
+ if r.indentWidth < startIndentWidth then
598
+ // Note: we can get here only if indentation is not significant
599
+ // If indentation is significant, we would see an <indent> as current token
600
+ // and the indent region would be Indented instead of InBraces.
601
+ //
602
+ // If indentation would be significant, an <indent> would be inserted here.
603
+ val t = body()
604
+ // Therefore, make sure there would be a matching <outdent>
605
+ def nextIndentWidth = in.indentWidth(in.next.offset)
606
+ if (in.token == NEWLINE || in.token == NEWLINES )
607
+ && ! (nextIndentWidth < startIndentWidth)
608
+ then
609
+ warning(
610
+ if startIndentWidth <= nextIndentWidth then
611
+ i """ Line is indented too far to the right, or a `{' is missing before:
612
+ |
613
+ | $t"""
614
+ else
615
+ in.spaceTabMismatchMsg(startIndentWidth, nextIndentWidth),
616
+ in.next.offset
617
+ )
618
+ t
619
+ else body()
620
+ case _ => body()
621
+
622
+ /** If indentation is not significant, check that this is not the start of a
623
+ * statement that's indented relative to the current region.
624
+ */
625
+ def checkNextNotIndented (): Unit = in.currentRegion match
626
+ case r : InBraces if in.token == NEWLINE || in.token == NEWLINES =>
627
+ val nextIndentWidth = in.indentWidth(in.next.offset)
628
+ if r.indentWidth < nextIndentWidth then
629
+ warning(i " Line is indented too far to the right, or a `{' is missing " , in.next.offset)
630
+ case _ =>
631
+
588
632
/* -------- REWRITES ----------------------------------------------------------- */
589
633
590
634
/** The last offset where a colon at the end of line would be required if a subsequent { ... }
@@ -1606,13 +1650,6 @@ object Parsers {
1606
1650
1607
1651
/* ----------- EXPRESSIONS ------------------------------------------------ */
1608
1652
1609
- /** EqualsExpr ::= `=' Expr
1610
- */
1611
- def equalsExpr (): Tree = {
1612
- accept(EQUALS )
1613
- expr()
1614
- }
1615
-
1616
1653
def condExpr (altToken : Token ): Tree =
1617
1654
if (in.token == LPAREN ) {
1618
1655
var t : Tree = atSpan(in.offset) { Parens (inParens(exprInParens())) }
@@ -1676,7 +1713,9 @@ object Parsers {
1676
1713
*/
1677
1714
val exprInParens : () => Tree = () => expr(Location .InParens )
1678
1715
1679
- def expr (): Tree = expr(Location .ElseWhere )
1716
+ val expr : () => Tree = () => expr(Location .ElseWhere )
1717
+
1718
+ def subExpr () = subPart(expr)
1680
1719
1681
1720
def expr (location : Location .Value ): Tree = {
1682
1721
val start = in.offset
@@ -1714,7 +1753,7 @@ object Parsers {
1714
1753
atSpan(in.skipToken()) {
1715
1754
val cond = condExpr(DO )
1716
1755
newLinesOpt()
1717
- val body = expr ()
1756
+ val body = subExpr ()
1718
1757
WhileDo (cond, body)
1719
1758
}
1720
1759
}
@@ -1753,7 +1792,7 @@ object Parsers {
1753
1792
if (in.token == CATCH ) {
1754
1793
val span = in.offset
1755
1794
in.nextToken()
1756
- (expr (), span)
1795
+ (subExpr (), span)
1757
1796
}
1758
1797
else (EmptyTree , - 1 )
1759
1798
@@ -1768,7 +1807,7 @@ object Parsers {
1768
1807
}
1769
1808
1770
1809
val finalizer =
1771
- if (in.token == FINALLY ) { in.nextToken(); expr () }
1810
+ if (in.token == FINALLY ) { in.nextToken(); subExpr () }
1772
1811
else {
1773
1812
if (handler.isEmpty) warning(
1774
1813
EmptyCatchAndFinallyBlock (body),
@@ -1823,7 +1862,7 @@ object Parsers {
1823
1862
case EQUALS =>
1824
1863
t match {
1825
1864
case Ident (_) | Select (_, _) | Apply (_, _) =>
1826
- atSpan(startOffset(t), in.skipToken()) { Assign (t, expr ()) }
1865
+ atSpan(startOffset(t), in.skipToken()) { Assign (t, subExpr ()) }
1827
1866
case _ =>
1828
1867
t
1829
1868
}
@@ -1870,8 +1909,8 @@ object Parsers {
1870
1909
atSpan(start, in.skipToken()) {
1871
1910
val cond = condExpr(THEN )
1872
1911
newLinesOpt()
1873
- val thenp = expr ()
1874
- val elsep = if (in.token == ELSE ) { in.nextToken(); expr () }
1912
+ val thenp = subExpr ()
1913
+ val elsep = if (in.token == ELSE ) { in.nextToken(); subExpr () }
1875
1914
else EmptyTree
1876
1915
mkIf(cond, thenp, elsep)
1877
1916
}
@@ -2224,7 +2263,7 @@ object Parsers {
2224
2263
else if (in.token == CASE ) generator()
2225
2264
else {
2226
2265
val pat = pattern1()
2227
- if (in.token == EQUALS ) atSpan(startOffset(pat), in.skipToken()) { GenAlias (pat, expr ()) }
2266
+ if (in.token == EQUALS ) atSpan(startOffset(pat), in.skipToken()) { GenAlias (pat, subExpr ()) }
2228
2267
else generatorRest(pat, casePat = false )
2229
2268
}
2230
2269
@@ -2241,7 +2280,7 @@ object Parsers {
2241
2280
if (casePat) GenCheckMode .FilterAlways
2242
2281
else if (ctx.settings.strict.value) GenCheckMode .Check
2243
2282
else GenCheckMode .FilterNow // filter for now, to keep backwards compat
2244
- GenFrom (pat, expr (), checkMode)
2283
+ GenFrom (pat, subExpr (), checkMode)
2245
2284
}
2246
2285
2247
2286
/** ForExpr ::= `for' (`(' Enumerators `)' | `{' Enumerators `}')
@@ -2313,12 +2352,12 @@ object Parsers {
2313
2352
newLinesOpt()
2314
2353
if (in.token == YIELD ) {
2315
2354
in.nextToken()
2316
- ForYield (enums, expr ())
2355
+ ForYield (enums, subExpr ())
2317
2356
}
2318
2357
else if (in.token == DO ) {
2319
2358
if (rewriteToOldSyntax()) dropTerminator()
2320
2359
in.nextToken()
2321
- ForDo (enums, expr ())
2360
+ ForDo (enums, subExpr ())
2322
2361
}
2323
2362
else {
2324
2363
if (! wrappedEnums) syntaxErrorOrIncomplete(YieldOrDoExpectedInForComprehension ())
@@ -2758,7 +2797,7 @@ object Parsers {
2758
2797
syntaxError(VarValParametersMayNotBeCallByName (name, mods.is(Mutable )))
2759
2798
val tpt = paramType()
2760
2799
val default =
2761
- if (in.token == EQUALS ) { in.nextToken(); expr () }
2800
+ if (in.token == EQUALS ) { in.nextToken(); subExpr () }
2762
2801
else EmptyTree
2763
2802
if (impliedMods.mods.nonEmpty)
2764
2803
impliedMods = impliedMods.withMods(Nil ) // keep only flags, so that parameter positions don't overlap
@@ -3003,7 +3042,7 @@ object Parsers {
3003
3042
(lhs.toList forall (_.isInstanceOf [Ident ])))
3004
3043
wildcardIdent()
3005
3044
else
3006
- expr ()
3045
+ subExpr ()
3007
3046
}
3008
3047
else EmptyTree
3009
3048
lhs match {
@@ -3043,7 +3082,7 @@ object Parsers {
3043
3082
if (in.isScala2Mode) newLineOptWhenFollowedBy(LBRACE )
3044
3083
val rhs = {
3045
3084
if (! (in.token == LBRACE && scala2ProcedureSyntax(" " ))) accept(EQUALS )
3046
- atSpan(in.offset) { constrExpr( ) }
3085
+ atSpan(in.offset) { subPart(constrExpr ) }
3047
3086
}
3048
3087
makeConstructor(Nil , vparamss, rhs).withMods(mods).setComment(in.getDocComment(start))
3049
3088
}
@@ -3076,7 +3115,7 @@ object Parsers {
3076
3115
if (in.token == EQUALS )
3077
3116
indentRegion(name) {
3078
3117
in.nextToken()
3079
- expr ()
3118
+ subExpr ()
3080
3119
}
3081
3120
else if (! tpt.isEmpty)
3082
3121
EmptyTree
@@ -3100,7 +3139,7 @@ object Parsers {
3100
3139
/** ConstrExpr ::= SelfInvocation
3101
3140
* | `{' SelfInvocation {semi BlockStat} `}'
3102
3141
*/
3103
- def constrExpr () : Tree =
3142
+ val constrExpr : () => Tree = () =>
3104
3143
if (in.isNestedStart)
3105
3144
atSpan(in.offset) {
3106
3145
inBracesOrIndented {
@@ -3351,7 +3390,7 @@ object Parsers {
3351
3390
if in.token == EQUALS && parents.length == 1 && parents.head.isType then
3352
3391
in.nextToken()
3353
3392
mods1 |= Final
3354
- DefDef (name, tparams, vparamss, parents.head, expr ())
3393
+ DefDef (name, tparams, vparamss, parents.head, subExpr ())
3355
3394
else
3356
3395
// println(i"given $name $hasExtensionParams $hasGivenSig")
3357
3396
possibleTemplateStart()
@@ -3431,22 +3470,27 @@ object Parsers {
3431
3470
3432
3471
/** TemplateOpt = [Template]
3433
3472
*/
3434
- def templateOpt (constr : DefDef ): Template = {
3473
+ def templateOpt (constr : DefDef ): Template =
3435
3474
possibleTemplateStart()
3436
- if ( in.token == EXTENDS || isIdent(nme.derives ))
3475
+ if in.token == EXTENDS || isIdent(nme.derives ) then
3437
3476
template(constr)
3438
3477
else
3439
- if (in.isNestedStart) template(constr)
3440
- else Template (constr, Nil , Nil , EmptyValDef , Nil )
3441
- }
3478
+ if in.isNestedStart then
3479
+ template(constr)
3480
+ else
3481
+ checkNextNotIndented()
3482
+ Template (constr, Nil , Nil , EmptyValDef , Nil )
3442
3483
3443
3484
/** TemplateBody ::= [nl] `{' TemplateStatSeq `}'
3444
3485
*/
3445
- def templateBodyOpt (constr : DefDef , parents : List [Tree ], derived : List [Tree ]): Template = {
3486
+ def templateBodyOpt (constr : DefDef , parents : List [Tree ], derived : List [Tree ]): Template =
3446
3487
val (self, stats) =
3447
- if (in.isNestedStart) templateBody() else (EmptyValDef , Nil )
3488
+ if in.isNestedStart then
3489
+ templateBody()
3490
+ else
3491
+ checkNextNotIndented()
3492
+ (EmptyValDef , Nil )
3448
3493
Template (constr, parents, derived, self, stats)
3449
- }
3450
3494
3451
3495
def templateBody (): (ValDef , List [Tree ]) = {
3452
3496
val r = inDefScopeBraces { templateStatSeq() }
0 commit comments