@@ -196,6 +196,7 @@ object Parsers {
196196 def isTemplateIntro = templateIntroTokens contains in.token
197197 def isDclIntro = dclIntroTokens contains in.token
198198 def isStatSeqEnd = in.isNestedEnd || in.token == EOF || in.token == RPAREN
199+ def isTemplateBodyStart = in.token == WITH || in.isNestedStart
199200 def mustStartStat = mustStartStatTokens contains in.token
200201
201202 /** Is current token a hard or soft modifier (in modifier position or not)? */
@@ -919,6 +920,39 @@ object Parsers {
919920 val next = in.lookahead.token
920921 next == LBRACKET || next == LPAREN
921922
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+
922956/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
923957
924958 var opStack : List [OpInfo ] = Nil
@@ -1281,14 +1315,14 @@ object Parsers {
12811315 in.sourcePos())
12821316 patch(source, Span (in.offset), " " )
12831317
1284- def possibleTemplateStart (isNew : Boolean = false ): Unit =
1318+ def possibleTemplateStart (): Unit =
12851319 in.observeColonEOL()
1286- if in.token == COLONEOL || in.token == WITH then
1320+ if in.token == COLONEOL then
12871321 if in.lookahead.isIdent(nme.end) then in.token = NEWLINE
12881322 else
12891323 in.nextToken()
12901324 if in.token != INDENT && in.token != LBRACE then
1291- syntaxErrorOrIncomplete(i " indented definitions expected, ${in} found " )
1325+ syntaxErrorOrIncomplete(ExpectedTokenButFound ( INDENT , in.token) )
12921326 else
12931327 newLineOptWhenFollowedBy(LBRACE )
12941328
@@ -2313,13 +2347,11 @@ object Parsers {
23132347 val start = in.skipToken()
23142348 def reposition (t : Tree ) = t.withSpan(Span (start, in.lastOffset))
23152349 possibleTemplateStart()
2316- val parents =
2317- if in.isNestedStart then Nil
2318- else constrApp() :: withConstrApps()
2350+ val parents = if isTemplateBodyStart then Nil else constrApp() :: withConstrApps()
23192351 colonAtEOLOpt()
2320- possibleTemplateStart(isNew = true )
2352+ possibleTemplateStart()
23212353 parents match {
2322- case parent :: Nil if ! in.isNestedStart =>
2354+ case parent :: Nil if ! isTemplateBodyStart =>
23232355 reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent)
23242356 case _ =>
23252357 New (reposition(templateBodyOpt(emptyConstructor, parents, Nil )))
@@ -3643,21 +3675,7 @@ object Parsers {
36433675 /** `{`with` ConstrApp} but no EOL allowed after `with`.
36443676 */
36453677 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
36613679 in.nextToken()
36623680 constrApp() :: withConstrApps()
36633681 else Nil
@@ -3701,32 +3719,66 @@ object Parsers {
37013719 template(constr)
37023720 else
37033721 possibleTemplateStart()
3704- if in.isNestedStart then
3722+ if isTemplateBodyStart then
37053723 template(constr)
37063724 else
37073725 checkNextNotIndented()
37083726 Template (constr, Nil , Nil , EmptyValDef , Nil )
37093727
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
37123732 */
37133733 def templateBodyOpt (constr : DefDef , parents : List [Tree ], derived : List [Tree ]): Template =
37143734 val (self, stats) =
3715- if in.isNestedStart then
3735+ if isTemplateBodyStart then
37163736 templateBody()
37173737 else
37183738 checkNextNotIndented()
37193739 (EmptyValDef , Nil )
37203740 Template (constr, parents, derived, self, stats)
37213741
37223742 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 )
37243749 if in.token == WITH then
37253750 syntaxError(EarlyDefinitionsNotSupported ())
37263751 in.nextToken()
37273752 template(emptyConstructor)
37283753 r
37293754
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+
37303782 /** with Template, with EOL <indent> interpreted */
37313783 def withTemplate (constr : DefDef , parents : List [Tree ]): Template =
37323784 if in.token != WITH then syntaxError(em " `with` expected " )
@@ -3800,10 +3852,10 @@ object Parsers {
38003852 * EnumStat ::= TemplateStat
38013853 * | Annotations Modifiers EnumCase
38023854 */
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
38053857 val stats = new ListBuffer [Tree ]
3806- if (isExprIntro && ! isDefIntro(modifierTokens)) {
3858+ if (self.isEmpty && isExprIntro && ! isDefIntro(modifierTokens)) {
38073859 val first = expr1()
38083860 if (in.token == ARROW ) {
38093861 first match {
@@ -3956,7 +4008,7 @@ object Parsers {
39564008 possibleTemplateStart()
39574009 if in.token == EOF then
39584010 ts += makePackaging(start, pkg, List ())
3959- else if in.isNestedStart then
4011+ else if isTemplateBodyStart then
39604012 ts += inDefScopeBraces(makePackaging(start, pkg, topStatSeq()), rewriteWithColon = true )
39614013 continue = true
39624014 else
@@ -3994,6 +4046,7 @@ object Parsers {
39944046 }
39954047
39964048 override def templateBody (): (ValDef , List [Thicket ]) = {
4049+ if in.token == WITH then in.nextToken()
39974050 skipBraces()
39984051 (EmptyValDef , List (EmptyTree ))
39994052 }
0 commit comments