@@ -196,6 +196,7 @@ object Parsers {
196
196
def isTemplateIntro = templateIntroTokens contains in.token
197
197
def isDclIntro = dclIntroTokens contains in.token
198
198
def isStatSeqEnd = in.isNestedEnd || in.token == EOF || in.token == RPAREN
199
+ def isTemplateBodyStart = in.token == WITH || in.isNestedStart
199
200
def mustStartStat = mustStartStatTokens contains in.token
200
201
201
202
/** Is current token a hard or soft modifier (in modifier position or not)? */
@@ -919,6 +920,39 @@ object Parsers {
919
920
val next = in.lookahead.token
920
921
next == LBRACKET || next == LPAREN
921
922
923
+ /** Does a template start after `with`? This is the case if either
924
+ * - the next token is `{`
925
+ * - the `with` is at the end of a line
926
+ * (except for source = 3.0-migration, when a warning is issued)
927
+ * - the next tokens is `<ident>` or `this` and the one after it is `:` or `=>`
928
+ * (i.e. we see the start of a self type)
929
+ */
930
+ def followingIsTemplateStart () =
931
+ val lookahead = in.LookaheadScanner ()
932
+ lookahead.nextToken()
933
+ lookahead.token == LBRACE
934
+ || lookahead.isAfterLineEnd
935
+ && {
936
+ if migrateTo3 then
937
+ warning(
938
+ em """ In Scala 3, `with` at the end of a line will start definitions,
939
+ |so it cannot be used in front of a parent constructor anymore.
940
+ |Place the `with` at the beginning of the next line instead. """ )
941
+ false
942
+ else
943
+ true
944
+ }
945
+ || (lookahead.isIdent || lookahead.token == THIS )
946
+ && {
947
+ lookahead.nextToken()
948
+ lookahead.token == COLON
949
+ && { // needed only as long as we support significant colon at eol
950
+ lookahead.nextToken()
951
+ ! lookahead.isAfterLineEnd
952
+ }
953
+ || lookahead.token == ARROW
954
+ }
955
+
922
956
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
923
957
924
958
var opStack : List [OpInfo ] = Nil
@@ -1281,14 +1315,14 @@ object Parsers {
1281
1315
in.sourcePos())
1282
1316
patch(source, Span (in.offset), " " )
1283
1317
1284
- def possibleTemplateStart (isNew : Boolean = false ): Unit =
1318
+ def possibleTemplateStart (): Unit =
1285
1319
in.observeColonEOL()
1286
- if in.token == COLONEOL || in.token == WITH then
1320
+ if in.token == COLONEOL then
1287
1321
if in.lookahead.isIdent(nme.end) then in.token = NEWLINE
1288
1322
else
1289
1323
in.nextToken()
1290
1324
if in.token != INDENT && in.token != LBRACE then
1291
- syntaxErrorOrIncomplete(i " indented definitions expected, ${in} found " )
1325
+ syntaxErrorOrIncomplete(ExpectedTokenButFound ( INDENT , in.token) )
1292
1326
else
1293
1327
newLineOptWhenFollowedBy(LBRACE )
1294
1328
@@ -2313,13 +2347,11 @@ object Parsers {
2313
2347
val start = in.skipToken()
2314
2348
def reposition (t : Tree ) = t.withSpan(Span (start, in.lastOffset))
2315
2349
possibleTemplateStart()
2316
- val parents =
2317
- if in.isNestedStart then Nil
2318
- else constrApp() :: withConstrApps()
2350
+ val parents = if isTemplateBodyStart then Nil else constrApp() :: withConstrApps()
2319
2351
colonAtEOLOpt()
2320
- possibleTemplateStart(isNew = true )
2352
+ possibleTemplateStart()
2321
2353
parents match {
2322
- case parent :: Nil if ! in.isNestedStart =>
2354
+ case parent :: Nil if ! isTemplateBodyStart =>
2323
2355
reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent)
2324
2356
case _ =>
2325
2357
New (reposition(templateBodyOpt(emptyConstructor, parents, Nil )))
@@ -3643,21 +3675,7 @@ object Parsers {
3643
3675
/** `{`with` ConstrApp} but no EOL allowed after `with`.
3644
3676
*/
3645
3677
def withConstrApps (): List [Tree ] =
3646
- def isTemplateStart =
3647
- val la = in.lookahead
3648
- la.token == LBRACE
3649
- || la.isAfterLineEnd
3650
- && {
3651
- if migrateTo3 then
3652
- warning(
3653
- em """ In Scala 3, `with` at the end of a line will start definitions,
3654
- |so it cannot be used in front of a parent constructor anymore.
3655
- |Place the `with` at the beginning of the next line instead. """ )
3656
- false
3657
- else
3658
- true
3659
- }
3660
- if in.token == WITH && ! isTemplateStart then
3678
+ if in.token == WITH && ! followingIsTemplateStart() then
3661
3679
in.nextToken()
3662
3680
constrApp() :: withConstrApps()
3663
3681
else Nil
@@ -3701,32 +3719,66 @@ object Parsers {
3701
3719
template(constr)
3702
3720
else
3703
3721
possibleTemplateStart()
3704
- if in.isNestedStart then
3722
+ if isTemplateBodyStart then
3705
3723
template(constr)
3706
3724
else
3707
3725
checkNextNotIndented()
3708
3726
Template (constr, Nil , Nil , EmptyValDef , Nil )
3709
3727
3710
- /** TemplateBody ::= [nl] `{' TemplateStatSeq `}'
3711
- * EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’
3728
+ /** TemplateBody ::= [nl | ‘with’] `{' TemplateStatSeq `}'
3729
+ * | ‘with’ [SelfType] indent TemplateStats outdent
3730
+ * EnumBody ::= [nl | ‘with’] ‘{’ [SelfType] EnumStats ‘}’
3731
+ * | ‘with’ [SelfType] indent EnumStats outdent
3712
3732
*/
3713
3733
def templateBodyOpt (constr : DefDef , parents : List [Tree ], derived : List [Tree ]): Template =
3714
3734
val (self, stats) =
3715
- if in.isNestedStart then
3735
+ if isTemplateBodyStart then
3716
3736
templateBody()
3717
3737
else
3718
3738
checkNextNotIndented()
3719
3739
(EmptyValDef , Nil )
3720
3740
Template (constr, parents, derived, self, stats)
3721
3741
3722
3742
def templateBody (): (ValDef , List [Tree ]) =
3723
- val r = inDefScopeBraces(templateStatSeq(), rewriteWithColon = true )
3743
+ val givenSelf =
3744
+ if in.token == WITH then
3745
+ in.nextToken()
3746
+ selfDefOpt()
3747
+ else EmptyValDef
3748
+ val r = inDefScopeBraces(templateStatSeq(givenSelf), rewriteWithColon = true )
3724
3749
if in.token == WITH then
3725
3750
syntaxError(EarlyDefinitionsNotSupported ())
3726
3751
in.nextToken()
3727
3752
template(emptyConstructor)
3728
3753
r
3729
3754
3755
+ /** SelfType ::= id [‘:’ InfixType] ‘=>’
3756
+ * | ‘this’ ‘:’ InfixType ‘=>’
3757
+ * Only called immediately after a `with`, in which case it must in turn
3758
+ * be followed by `INDENT`.
3759
+ */
3760
+ def selfDefOpt (): ValDef = atSpan(in.offset) {
3761
+ val vd =
3762
+ if in.isIdent then
3763
+ val selfName = ident()
3764
+ if in.token == COLON then
3765
+ in.nextToken()
3766
+ makeSelfDef(selfName, infixType())
3767
+ else
3768
+ makeSelfDef(selfName, TypeTree ())
3769
+ else if in.token == THIS then
3770
+ in.nextToken()
3771
+ accept(COLON )
3772
+ makeSelfDef(nme.WILDCARD , infixType())
3773
+ else
3774
+ EmptyValDef
3775
+ if ! vd.isEmpty then
3776
+ accept(ARROW )
3777
+ if in.token != INDENT then
3778
+ syntaxErrorOrIncomplete(ExpectedTokenButFound (INDENT , in.token))
3779
+ vd
3780
+ }
3781
+
3730
3782
/** with Template, with EOL <indent> interpreted */
3731
3783
def withTemplate (constr : DefDef , parents : List [Tree ]): Template =
3732
3784
if in.token != WITH then syntaxError(em " `with` expected " )
@@ -3800,10 +3852,10 @@ object Parsers {
3800
3852
* EnumStat ::= TemplateStat
3801
3853
* | Annotations Modifiers EnumCase
3802
3854
*/
3803
- def templateStatSeq (): (ValDef , List [Tree ]) = checkNoEscapingPlaceholders {
3804
- var self : ValDef = EmptyValDef
3855
+ def templateStatSeq (givenSelf : ValDef = EmptyValDef ): (ValDef , List [Tree ]) = checkNoEscapingPlaceholders {
3856
+ var self = givenSelf
3805
3857
val stats = new ListBuffer [Tree ]
3806
- if (isExprIntro && ! isDefIntro(modifierTokens)) {
3858
+ if (self.isEmpty && isExprIntro && ! isDefIntro(modifierTokens)) {
3807
3859
val first = expr1()
3808
3860
if (in.token == ARROW ) {
3809
3861
first match {
@@ -3956,7 +4008,7 @@ object Parsers {
3956
4008
possibleTemplateStart()
3957
4009
if in.token == EOF then
3958
4010
ts += makePackaging(start, pkg, List ())
3959
- else if in.isNestedStart then
4011
+ else if isTemplateBodyStart then
3960
4012
ts += inDefScopeBraces(makePackaging(start, pkg, topStatSeq()), rewriteWithColon = true )
3961
4013
continue = true
3962
4014
else
@@ -3994,6 +4046,7 @@ object Parsers {
3994
4046
}
3995
4047
3996
4048
override def templateBody (): (ValDef , List [Thicket ]) = {
4049
+ if in.token == WITH then in.nextToken()
3997
4050
skipBraces()
3998
4051
(EmptyValDef , List (EmptyTree ))
3999
4052
}
0 commit comments