@@ -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,53 @@ object Parsers {
919
920
val next = in.lookahead.token
920
921
next == LBRACKET || next == LPAREN
921
922
923
+ private def withEndMigrationWarning (): Boolean =
924
+ migrateTo3
925
+ && {
926
+ warning(
927
+ em """ In Scala 3, `with` at the end of a line will start definitions,
928
+ |so it cannot be used in front of a parent constructor anymore.
929
+ |Place the `with` at the beginning of the next line instead. """ )
930
+ true
931
+ }
932
+
933
+ /** Does a template start after `with`? This is the case if either
934
+ * - the next token is `{`
935
+ * - the `with` is at the end of a line
936
+ * (except for source = 3.0-migration, when a warning is issued)
937
+ * - the next tokens is `<ident>` or `this` and the one after it is `:` or `=>`
938
+ * (i.e. we see the start of a self type)
939
+ */
940
+ def followingIsTemplateStart () =
941
+ val lookahead = in.LookaheadScanner ()
942
+ lookahead.nextToken()
943
+ lookahead.token == LBRACE
944
+ || lookahead.isAfterLineEnd && ! withEndMigrationWarning()
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
+
956
+ /** Does a refinement start after `with`? This is the case if either
957
+ * - the next token is `{`
958
+ * - the `with` is at the end of a line and is followed by a token that starts a declaration
959
+ */
960
+ def followingIsRefinementStart () =
961
+ val lookahead = in.LookaheadScanner ()
962
+ lookahead.nextToken()
963
+ lookahead.token == LBRACE
964
+ || lookahead.isAfterLineEnd
965
+ && {
966
+ if lookahead.token == INDENT then lookahead.nextToken()
967
+ dclIntroTokens.contains(lookahead.token)
968
+ }
969
+
922
970
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
923
971
924
972
var opStack : List [OpInfo ] = Nil
@@ -1281,14 +1329,14 @@ object Parsers {
1281
1329
in.sourcePos())
1282
1330
patch(source, Span (in.offset), " " )
1283
1331
1284
- def possibleTemplateStart (isNew : Boolean = false ): Unit =
1332
+ def possibleTemplateStart (): Unit =
1285
1333
in.observeColonEOL()
1286
- if in.token == COLONEOL || in.token == WITH then
1334
+ if in.token == COLONEOL then
1287
1335
if in.lookahead.isIdent(nme.end) then in.token = NEWLINE
1288
1336
else
1289
1337
in.nextToken()
1290
1338
if in.token != INDENT && in.token != LBRACE then
1291
- syntaxErrorOrIncomplete(i " indented definitions expected, ${in} " )
1339
+ syntaxErrorOrIncomplete(ExpectedTokenButFound ( INDENT , in.token) )
1292
1340
else
1293
1341
newLineOptWhenFollowedBy(LBRACE )
1294
1342
@@ -1532,33 +1580,29 @@ object Parsers {
1532
1580
def infixTypeRest (t : Tree ): Tree =
1533
1581
infixOps(t, canStartTypeTokens, refinedType, isType = true , isOperator = ! isPostfixStar)
1534
1582
1535
- /** RefinedType ::= WithType {[nl] Refinement}
1583
+ /** RefinedType ::= WithType {[nl | ‘with’ ] Refinement}
1536
1584
*/
1537
1585
val refinedType : () => Tree = () => refinedTypeRest(withType())
1538
1586
1539
- def refinedTypeRest (t : Tree ): Tree = {
1587
+ def refinedTypeRest (t : Tree ): Tree =
1540
1588
argumentStart()
1541
- if (in.isNestedStart)
1589
+ if isTemplateBodyStart then
1590
+ if in.token == WITH then in.nextToken()
1542
1591
refinedTypeRest(atSpan(startOffset(t)) {
1543
1592
RefinedTypeTree (rejectWildcardType(t), refinement(indentOK = true ))
1544
1593
})
1545
1594
else t
1546
- }
1547
1595
1548
1596
/** WithType ::= AnnotType {`with' AnnotType} (deprecated)
1549
1597
*/
1550
1598
def withType (): Tree = withTypeRest(annotType())
1551
1599
1552
1600
def withTypeRest (t : Tree ): Tree =
1553
- if in.token == WITH then
1554
- val withOffset = in.offset
1555
- in.nextToken()
1556
- if in.token == LBRACE || in.token == INDENT then
1557
- t
1558
- else
1559
- if sourceVersion.isAtLeast(`3.1`) then
1560
- deprecationWarning(DeprecatedWithOperator (), withOffset)
1561
- atSpan(startOffset(t)) { makeAndType(t, withType()) }
1601
+ if in.token == WITH && ! followingIsRefinementStart() then
1602
+ in.nextTokenNoIndent()
1603
+ if sourceVersion.isAtLeast(`3.1`) then
1604
+ deprecationWarning(DeprecatedWithOperator ())
1605
+ atSpan(startOffset(t)) { makeAndType(t, withType()) }
1562
1606
else t
1563
1607
1564
1608
/** AnnotType ::= SimpleType {Annotation}
@@ -2313,13 +2357,11 @@ object Parsers {
2313
2357
val start = in.skipToken()
2314
2358
def reposition (t : Tree ) = t.withSpan(Span (start, in.lastOffset))
2315
2359
possibleTemplateStart()
2316
- val parents =
2317
- if in.isNestedStart then Nil
2318
- else constrApps(commaOK = false )
2360
+ val parents = if isTemplateBodyStart then Nil else constrApp() :: withConstrApps()
2319
2361
colonAtEOLOpt()
2320
- possibleTemplateStart(isNew = true )
2362
+ possibleTemplateStart()
2321
2363
parents match {
2322
- case parent :: Nil if ! in.isNestedStart =>
2364
+ case parent :: Nil if ! isTemplateBodyStart =>
2323
2365
reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent)
2324
2366
case _ =>
2325
2367
New (reposition(templateBodyOpt(emptyConstructor, parents, Nil )))
@@ -3494,7 +3536,7 @@ object Parsers {
3494
3536
val parents =
3495
3537
if (in.token == EXTENDS ) {
3496
3538
in.nextToken()
3497
- constrApps(commaOK = true )
3539
+ constrApps()
3498
3540
}
3499
3541
else Nil
3500
3542
Template (constr, parents, Nil , EmptyValDef , Nil )
@@ -3517,7 +3559,7 @@ object Parsers {
3517
3559
syntaxError(i " extension clause can only define methods " , stat.span)
3518
3560
}
3519
3561
3520
- /** GivenDef ::= [GivenSig] (Type [‘=’ Expr] | StructuralInstance )
3562
+ /** GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | ConstrApps TemplateBody )
3521
3563
* GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘:’
3522
3564
*/
3523
3565
def givenDef (start : Offset , mods : Modifiers , givenMod : Mod ) = atSpan(start, nameStart) {
@@ -3536,9 +3578,10 @@ object Parsers {
3536
3578
val noParams = tparams.isEmpty && vparamss.isEmpty
3537
3579
if ! (name.isEmpty && noParams) then accept(COLON )
3538
3580
val parents =
3539
- if isSimpleLiteral then toplevelTyp( ) :: Nil
3540
- else constrApp() :: withConstrApps ()
3581
+ if isSimpleLiteral then rejectWildcardType(annotType() ) :: Nil
3582
+ else constrApps ()
3541
3583
val parentsIsType = parents.length == 1 && parents.head.isType
3584
+ newLineOptWhenFollowedBy(LBRACE )
3542
3585
if in.token == EQUALS && parentsIsType then
3543
3586
accept(EQUALS )
3544
3587
mods1 |= Final
@@ -3547,17 +3590,17 @@ object Parsers {
3547
3590
ValDef (name, parents.head, subExpr())
3548
3591
else
3549
3592
DefDef (name, joinParams(tparams, vparamss), parents.head, subExpr())
3550
- else if in.token != WITH && parentsIsType then
3551
- if name.isEmpty then
3552
- syntaxError(em " anonymous given cannot be abstract " )
3553
- DefDef (name, joinParams(tparams, vparamss), parents.head, EmptyTree )
3554
- else
3593
+ else if isTemplateBodyStart then
3555
3594
val tparams1 = tparams.map(tparam => tparam.withMods(tparam.mods | PrivateLocal ))
3556
3595
val vparamss1 = vparamss.map(_.map(vparam =>
3557
3596
vparam.withMods(vparam.mods &~ Param | ParamAccessor | Protected )))
3558
- val templ = withTemplate (makeConstructor(tparams1, vparamss1), parents)
3597
+ val templ = templateBodyOpt (makeConstructor(tparams1, vparamss1), parents, Nil )
3559
3598
if noParams then ModuleDef (name, templ)
3560
3599
else TypeDef (name.toTypeName, templ)
3600
+ else
3601
+ if name.isEmpty then
3602
+ syntaxError(em " anonymous given cannot be abstract " )
3603
+ DefDef (name, joinParams(tparams, vparamss), parents.head, EmptyTree )
3561
3604
end gdef
3562
3605
finalizeDef(gdef, mods1, start)
3563
3606
}
@@ -3576,8 +3619,11 @@ object Parsers {
3576
3619
isUsingClause(extParams)
3577
3620
do ()
3578
3621
leadParamss ++= paramClauses(givenOnly = true , numLeadParams = nparams)
3579
- if in.token == COLON then
3580
- syntaxError(" no `:` expected here" )
3622
+ if in.token == WITH then
3623
+ syntaxError(
3624
+ i """ No `with` expected here.
3625
+ |
3626
+ |An extension clause is simply followed by one or more method definitions. """ )
3581
3627
in.nextToken()
3582
3628
val methods =
3583
3629
if isDefIntro(modifierTokens) then
@@ -3626,25 +3672,24 @@ object Parsers {
3626
3672
// Using Ident(tpnme.ERROR) to avoid causing cascade errors on non-user-written code
3627
3673
if in.token == LPAREN then parArgumentExprss(wrapNew(t)) else t
3628
3674
3629
- /** ConstrApps ::= ConstrApp {( ‘,’ | ‘with’) ConstrApp}
3675
+ /** ConstrApps ::= ConstrApp ({ ‘,’ ConstrApp} | { ‘with’ ConstrApp})
3630
3676
*/
3631
- def constrApps (commaOK : Boolean ): List [Tree ] =
3677
+ def constrApps (): List [Tree ] =
3632
3678
val t = constrApp()
3633
- val ts =
3634
- if in.token == WITH || commaOK && in.token == COMMA then
3635
- in.nextToken()
3636
- constrApps(commaOK)
3637
- else Nil
3679
+ val ts = if in.token == COMMA then commaConstrApps() else withConstrApps()
3638
3680
t :: ts
3639
3681
3682
+ /** `{`,` ConstrApp} */
3683
+ def commaConstrApps (): List [Tree ] =
3684
+ if in.token == COMMA then
3685
+ in.nextToken()
3686
+ constrApp() :: commaConstrApps()
3687
+ else Nil
3640
3688
3641
3689
/** `{`with` ConstrApp} but no EOL allowed after `with`.
3642
3690
*/
3643
3691
def withConstrApps (): List [Tree ] =
3644
- def isTemplateStart =
3645
- val la = in.lookahead
3646
- la.isAfterLineEnd || la.token == LBRACE
3647
- if in.token == WITH && ! isTemplateStart then
3692
+ if in.token == WITH && ! followingIsTemplateStart() then
3648
3693
in.nextToken()
3649
3694
constrApp() :: withConstrApps()
3650
3695
else Nil
@@ -3662,7 +3707,7 @@ object Parsers {
3662
3707
in.sourcePos())
3663
3708
Nil
3664
3709
}
3665
- else constrApps(commaOK = true )
3710
+ else constrApps()
3666
3711
}
3667
3712
else Nil
3668
3713
newLinesOptWhenFollowedBy(nme.derives )
@@ -3688,39 +3733,65 @@ object Parsers {
3688
3733
template(constr)
3689
3734
else
3690
3735
possibleTemplateStart()
3691
- if in.isNestedStart then
3736
+ if isTemplateBodyStart then
3692
3737
template(constr)
3693
3738
else
3694
3739
checkNextNotIndented()
3695
3740
Template (constr, Nil , Nil , EmptyValDef , Nil )
3696
3741
3697
- /** TemplateBody ::= [nl] `{' TemplateStatSeq `}'
3698
- * EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’
3742
+ /** TemplateBody ::= [nl | ‘with’] `{' TemplateStatSeq `}'
3743
+ * | ‘with’ [SelfType] indent TemplateStats outdent
3744
+ * EnumBody ::= [nl | ‘with’] ‘{’ [SelfType] EnumStats ‘}’
3745
+ * | ‘with’ [SelfType] indent EnumStats outdent
3699
3746
*/
3700
3747
def templateBodyOpt (constr : DefDef , parents : List [Tree ], derived : List [Tree ]): Template =
3701
3748
val (self, stats) =
3702
- if in.isNestedStart then
3749
+ if isTemplateBodyStart then
3703
3750
templateBody()
3704
3751
else
3705
3752
checkNextNotIndented()
3706
3753
(EmptyValDef , Nil )
3707
3754
Template (constr, parents, derived, self, stats)
3708
3755
3709
3756
def templateBody (): (ValDef , List [Tree ]) =
3710
- val r = inDefScopeBraces(templateStatSeq(), rewriteWithColon = true )
3757
+ val givenSelf =
3758
+ if in.token == WITH then
3759
+ in.nextToken()
3760
+ selfDefOpt()
3761
+ else EmptyValDef
3762
+ val r = inDefScopeBraces(templateStatSeq(givenSelf), rewriteWithColon = true )
3711
3763
if in.token == WITH then
3712
3764
syntaxError(EarlyDefinitionsNotSupported ())
3713
3765
in.nextToken()
3714
3766
template(emptyConstructor)
3715
3767
r
3716
3768
3717
- /** with Template, with EOL <indent> interpreted */
3718
- def withTemplate (constr : DefDef , parents : List [Tree ]): Template =
3719
- if in.token != WITH then syntaxError(em " `with` expected " )
3720
- possibleTemplateStart() // consumes a WITH token
3721
- val (self, stats) = templateBody()
3722
- Template (constr, parents, Nil , self, stats)
3723
- .withSpan(Span (constr.span.orElse(parents.head.span).start, in.lastOffset))
3769
+ /** SelfType ::= id [‘:’ InfixType] ‘=>’
3770
+ * | ‘this’ ‘:’ InfixType ‘=>’
3771
+ * Only called immediately after a `with`, in which case it must in turn
3772
+ * be followed by `INDENT`.
3773
+ */
3774
+ def selfDefOpt (): ValDef = atSpan(in.offset) {
3775
+ val vd =
3776
+ if in.isIdent then
3777
+ val selfName = ident()
3778
+ if in.token == COLON then
3779
+ in.nextToken()
3780
+ makeSelfDef(selfName, infixType())
3781
+ else
3782
+ makeSelfDef(selfName, TypeTree ())
3783
+ else if in.token == THIS then
3784
+ in.nextToken()
3785
+ accept(COLON )
3786
+ makeSelfDef(nme.WILDCARD , infixType())
3787
+ else
3788
+ EmptyValDef
3789
+ if ! vd.isEmpty then
3790
+ accept(ARROW )
3791
+ if in.token != INDENT then
3792
+ syntaxErrorOrIncomplete(ExpectedTokenButFound (INDENT , in.token))
3793
+ vd
3794
+ }
3724
3795
3725
3796
/* -------- STATSEQS ------------------------------------------- */
3726
3797
@@ -3787,10 +3858,10 @@ object Parsers {
3787
3858
* EnumStat ::= TemplateStat
3788
3859
* | Annotations Modifiers EnumCase
3789
3860
*/
3790
- def templateStatSeq (): (ValDef , List [Tree ]) = checkNoEscapingPlaceholders {
3791
- var self : ValDef = EmptyValDef
3861
+ def templateStatSeq (givenSelf : ValDef = EmptyValDef ): (ValDef , List [Tree ]) = checkNoEscapingPlaceholders {
3862
+ var self = givenSelf
3792
3863
val stats = new ListBuffer [Tree ]
3793
- if (isExprIntro && ! isDefIntro(modifierTokens)) {
3864
+ if (self.isEmpty && isExprIntro && ! isDefIntro(modifierTokens)) {
3794
3865
val first = expr1()
3795
3866
if (in.token == ARROW ) {
3796
3867
first match {
@@ -3801,12 +3872,20 @@ object Parsers {
3801
3872
if (name != nme.ERROR )
3802
3873
self = makeSelfDef(name, tpt).withSpan(first.span)
3803
3874
}
3804
- in.token = SELFARROW // suppresses INDENT insertion after `=>`
3805
- in.nextToken()
3875
+ in.nextTokenNoIndent()
3806
3876
}
3807
3877
else {
3808
3878
stats += first
3809
- acceptStatSepUnlessAtEnd(stats)
3879
+ if in.token == WITH then
3880
+ syntaxError(
3881
+ i """ end of statement expected but ${showToken(WITH )} found
3882
+ |
3883
+ |Maybe you meant to write a mixin in an extends clause?
3884
+ |Note that this requires the `with` to come first now.
3885
+ |I.e.
3886
+ |
3887
+ | with $first""" )
3888
+ else acceptStatSepUnlessAtEnd(stats)
3810
3889
}
3811
3890
}
3812
3891
var exitOnError = false
@@ -3934,7 +4013,7 @@ object Parsers {
3934
4013
possibleTemplateStart()
3935
4014
if in.token == EOF then
3936
4015
ts += makePackaging(start, pkg, List ())
3937
- else if in.isNestedStart then
4016
+ else if isTemplateBodyStart then
3938
4017
ts += inDefScopeBraces(makePackaging(start, pkg, topStatSeq()), rewriteWithColon = true )
3939
4018
continue = true
3940
4019
else
@@ -3972,6 +4051,7 @@ object Parsers {
3972
4051
}
3973
4052
3974
4053
override def templateBody (): (ValDef , List [Thicket ]) = {
4054
+ if in.token == WITH then in.nextToken()
3975
4055
skipBraces()
3976
4056
(EmptyValDef , List (EmptyTree ))
3977
4057
}
0 commit comments