@@ -439,6 +439,14 @@ object Parsers {
439
439
finally staged = saved
440
440
}
441
441
442
+ private var strictIndent = false
443
+ private def withStrictIndent [T ](body : => T ): T = {
444
+ val saved = strictIndent
445
+ strictIndent = true
446
+ try body
447
+ finally strictIndent = saved
448
+ }
449
+
442
450
/* ---------- TREE CONSTRUCTION ------------------------------------------- */
443
451
444
452
/** Convert tree to formal parameter list
@@ -1281,16 +1289,21 @@ object Parsers {
1281
1289
in.sourcePos())
1282
1290
patch(source, Span (in.offset), " " )
1283
1291
1284
- def possibleTemplateStart (isNew : Boolean = false ): Unit =
1292
+ def possibleTemplateStart [ A ] (isNew : Boolean = false )( rest : => A ) : A =
1285
1293
in.observeColonEOL()
1286
- if in.token == COLONEOL || in.token == WITHEOL then
1287
- if in.lookahead.isIdent(nme.end) then in.token = NEWLINE
1294
+ val indented =
1295
+ if in.token == COLONEOL || in.token == WITHEOL then
1296
+ if in.lookahead.isIdent(nme.end) then in.token = NEWLINE
1297
+ else
1298
+ in.nextToken()
1299
+ if in.token != INDENT && in.token != LBRACE then
1300
+ syntaxErrorOrIncomplete(i " indented definitions expected, ${in}" )
1301
+ true
1288
1302
else
1289
- in.nextToken()
1290
- if in.token != INDENT && in.token != LBRACE then
1291
- syntaxErrorOrIncomplete(i " indented definitions expected, ${in}" )
1292
- else
1293
- newLineOptWhenFollowedBy(LBRACE )
1303
+ newLineOptWhenFollowedBy(LBRACE )
1304
+ false
1305
+ if indented then withStrictIndent(rest)
1306
+ else rest
1294
1307
1295
1308
def checkEndMarker [T <: Tree ](stats : ListBuffer [T ]): Unit =
1296
1309
@@ -2309,19 +2322,22 @@ object Parsers {
2309
2322
def newExpr (): Tree =
2310
2323
val start = in.skipToken()
2311
2324
def reposition (t : Tree ) = t.withSpan(Span (start, in.lastOffset))
2312
- possibleTemplateStart()
2313
- val parents =
2314
- if in.isNestedStart then Nil
2315
- else constrApps(commaOK = false )
2316
- colonAtEOLOpt()
2317
- possibleTemplateStart(isNew = true )
2318
- parents match {
2319
- case parent :: Nil if ! in.isNestedStart =>
2320
- reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent)
2321
- case _ =>
2322
- New (reposition(templateBodyOpt(emptyConstructor, parents, Nil )))
2325
+ possibleTemplateStart() {
2326
+ val parents =
2327
+ if in.isNestedStart then Nil
2328
+ else constrApps(commaOK = false )
2329
+ colonAtEOLOpt()
2330
+ possibleTemplateStart(isNew = true ) {
2331
+ parents match {
2332
+ case parent :: Nil if ! in.isNestedStart =>
2333
+ reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent)
2334
+ case _ =>
2335
+ New (reposition(templateBodyOpt(emptyConstructor, parents, Nil )))
2336
+ }
2337
+ }
2323
2338
}
2324
2339
2340
+
2325
2341
/** ExprsInParens ::= ExprInParens {`,' ExprInParens}
2326
2342
*/
2327
2343
def exprsInParensOpt (): List [Tree ] =
@@ -3660,12 +3676,13 @@ object Parsers {
3660
3676
tokenSeparated(COMMA , () => convertToTypeId(qualId()))
3661
3677
}
3662
3678
else Nil
3663
- possibleTemplateStart()
3664
- if (isEnum) {
3665
- val (self, stats) = withinEnum(templateBody())
3666
- Template (constr, parents, derived, self, stats)
3679
+ possibleTemplateStart() {
3680
+ if (isEnum) {
3681
+ val (self, stats) = withinEnum(templateBody())
3682
+ Template (constr, parents, derived, self, stats)
3683
+ }
3684
+ else templateBodyOpt(constr, parents, derived)
3667
3685
}
3668
- else templateBodyOpt(constr, parents, derived)
3669
3686
}
3670
3687
3671
3688
/** TemplateOpt = [Template]
@@ -3675,12 +3692,13 @@ object Parsers {
3675
3692
if in.token == EXTENDS || isIdent(nme.derives ) then
3676
3693
template(constr)
3677
3694
else
3678
- possibleTemplateStart()
3679
- if in.isNestedStart then
3680
- template(constr)
3681
- else
3682
- checkNextNotIndented()
3683
- Template (constr, Nil , Nil , EmptyValDef , Nil )
3695
+ possibleTemplateStart() {
3696
+ if in.isNestedStart then
3697
+ template(constr)
3698
+ else
3699
+ checkNextNotIndented()
3700
+ Template (constr, Nil , Nil , EmptyValDef , Nil )
3701
+ }
3684
3702
3685
3703
/** TemplateBody ::= [nl] `{' TemplateStatSeq `}'
3686
3704
* EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’
@@ -3705,10 +3723,11 @@ object Parsers {
3705
3723
/** with Template, with EOL <indent> interpreted */
3706
3724
def withTemplate (constr : DefDef , parents : List [Tree ]): Template =
3707
3725
if in.token != WITHEOL then accept(WITH )
3708
- possibleTemplateStart() // consumes a WITHEOL token
3709
- val (self, stats) = templateBody()
3710
- Template (constr, parents, Nil , self, stats)
3711
- .withSpan(Span (constr.span.orElse(parents.head.span).start, in.lastOffset))
3726
+ possibleTemplateStart() { // consumes a WITHEOL token
3727
+ val (self, stats) = templateBody()
3728
+ Template (constr, parents, Nil , self, stats)
3729
+ .withSpan(Span (constr.span.orElse(parents.head.span).start, in.lastOffset))
3730
+ }
3712
3731
3713
3732
/* -------- STATSEQS ------------------------------------------- */
3714
3733
@@ -3778,6 +3797,7 @@ object Parsers {
3778
3797
def templateStatSeq (): (ValDef , List [Tree ]) = checkNoEscapingPlaceholders {
3779
3798
var self : ValDef = EmptyValDef
3780
3799
val stats = new ListBuffer [Tree ]
3800
+ var firstStatIndent : Option [IndentWidth ] = None
3781
3801
if (isExprIntro && ! isDefIntro(modifierTokens)) {
3782
3802
val first = expr1()
3783
3803
if (in.token == ARROW ) {
@@ -3793,13 +3813,22 @@ object Parsers {
3793
3813
in.nextToken()
3794
3814
}
3795
3815
else {
3816
+ val indent = in.indentWidth(in.offset)
3817
+ if (firstStatIndent.isEmpty) firstStatIndent = Some (indent)
3818
+ else ()
3796
3819
stats += first
3797
3820
acceptStatSepUnlessAtEnd(stats)
3798
3821
}
3799
3822
}
3800
3823
var exitOnError = false
3801
3824
while (! isStatSeqEnd && ! exitOnError) {
3802
3825
setLastStatOffset()
3826
+ val indent = in.indentWidth(in.offset)
3827
+ if (firstStatIndent.isEmpty) firstStatIndent = Some (indent)
3828
+ else if (strictIndent && firstStatIndent != Some (indent))
3829
+ syntaxErrorOrIncomplete(s " unexpected indent: found $indent expected ${firstStatIndent.get}" )
3830
+ else ()
3831
+
3803
3832
if (in.token == IMPORT )
3804
3833
stats ++= importClause(IMPORT , mkImport())
3805
3834
else if (in.token == EXPORT )
@@ -3919,18 +3948,19 @@ object Parsers {
3919
3948
else
3920
3949
val pkg = qualId()
3921
3950
var continue = false
3922
- possibleTemplateStart()
3923
- if in.token == EOF then
3924
- ts += makePackaging(start, pkg, List ())
3925
- else if in.isNestedStart then
3926
- ts += inDefScopeBraces(makePackaging(start, pkg, topStatSeq()), rewriteWithColon = true )
3927
- continue = true
3928
- else
3929
- acceptStatSep()
3930
- ts += makePackaging(start, pkg, topstats())
3931
- if continue then
3932
- acceptStatSepUnlessAtEnd(ts)
3933
- ts ++= topStatSeq()
3951
+ possibleTemplateStart() {
3952
+ if in.token == EOF then
3953
+ ts += makePackaging(start, pkg, List ())
3954
+ else if in.isNestedStart then
3955
+ ts += inDefScopeBraces(makePackaging(start, pkg, topStatSeq()), rewriteWithColon = true )
3956
+ continue = true
3957
+ else
3958
+ acceptStatSep()
3959
+ ts += makePackaging(start, pkg, topstats())
3960
+ if continue then
3961
+ acceptStatSepUnlessAtEnd(ts)
3962
+ ts ++= topStatSeq()
3963
+ }
3934
3964
}
3935
3965
else
3936
3966
ts ++= topStatSeq(outermost = true )
0 commit comments