@@ -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,53 @@ object Parsers {
919920 val next = in.lookahead.token
920921 next == LBRACKET || next == LPAREN
921922
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+
922970/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
923971
924972 var opStack : List [OpInfo ] = Nil
@@ -1281,14 +1329,14 @@ object Parsers {
12811329 in.sourcePos())
12821330 patch(source, Span (in.offset), " " )
12831331
1284- def possibleTemplateStart (isNew : Boolean = false ): Unit =
1332+ def possibleTemplateStart (): Unit =
12851333 in.observeColonEOL()
1286- if in.token == COLONEOL || in.token == WITH then
1334+ if in.token == COLONEOL then
12871335 if in.lookahead.isIdent(nme.end) then in.token = NEWLINE
12881336 else
12891337 in.nextToken()
12901338 if in.token != INDENT && in.token != LBRACE then
1291- syntaxErrorOrIncomplete(i " indented definitions expected, ${in} " )
1339+ syntaxErrorOrIncomplete(ExpectedTokenButFound ( INDENT , in.token) )
12921340 else
12931341 newLineOptWhenFollowedBy(LBRACE )
12941342
@@ -1532,33 +1580,29 @@ object Parsers {
15321580 def infixTypeRest (t : Tree ): Tree =
15331581 infixOps(t, canStartTypeTokens, refinedType, isType = true , isOperator = ! isPostfixStar)
15341582
1535- /** RefinedType ::= WithType {[nl] Refinement}
1583+ /** RefinedType ::= WithType {[nl | ‘with’ ] Refinement}
15361584 */
15371585 val refinedType : () => Tree = () => refinedTypeRest(withType())
15381586
1539- def refinedTypeRest (t : Tree ): Tree = {
1587+ def refinedTypeRest (t : Tree ): Tree =
15401588 argumentStart()
1541- if (in.isNestedStart)
1589+ if isTemplateBodyStart then
1590+ if in.token == WITH then in.nextToken()
15421591 refinedTypeRest(atSpan(startOffset(t)) {
15431592 RefinedTypeTree (rejectWildcardType(t), refinement(indentOK = true ))
15441593 })
15451594 else t
1546- }
15471595
15481596 /** WithType ::= AnnotType {`with' AnnotType} (deprecated)
15491597 */
15501598 def withType (): Tree = withTypeRest(annotType())
15511599
15521600 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()) }
15621606 else t
15631607
15641608 /** AnnotType ::= SimpleType {Annotation}
@@ -2313,13 +2357,11 @@ object Parsers {
23132357 val start = in.skipToken()
23142358 def reposition (t : Tree ) = t.withSpan(Span (start, in.lastOffset))
23152359 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()
23192361 colonAtEOLOpt()
2320- possibleTemplateStart(isNew = true )
2362+ possibleTemplateStart()
23212363 parents match {
2322- case parent :: Nil if ! in.isNestedStart =>
2364+ case parent :: Nil if ! isTemplateBodyStart =>
23232365 reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent)
23242366 case _ =>
23252367 New (reposition(templateBodyOpt(emptyConstructor, parents, Nil )))
@@ -3494,7 +3536,7 @@ object Parsers {
34943536 val parents =
34953537 if (in.token == EXTENDS ) {
34963538 in.nextToken()
3497- constrApps(commaOK = true )
3539+ constrApps()
34983540 }
34993541 else Nil
35003542 Template (constr, parents, Nil , EmptyValDef , Nil )
@@ -3517,7 +3559,7 @@ object Parsers {
35173559 syntaxError(i " extension clause can only define methods " , stat.span)
35183560 }
35193561
3520- /** GivenDef ::= [GivenSig] (Type [‘=’ Expr] | StructuralInstance )
3562+ /** GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | ConstrApps TemplateBody )
35213563 * GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘:’
35223564 */
35233565 def givenDef (start : Offset , mods : Modifiers , givenMod : Mod ) = atSpan(start, nameStart) {
@@ -3536,9 +3578,10 @@ object Parsers {
35363578 val noParams = tparams.isEmpty && vparamss.isEmpty
35373579 if ! (name.isEmpty && noParams) then accept(COLON )
35383580 val parents =
3539- if isSimpleLiteral then toplevelTyp( ) :: Nil
3540- else constrApp() :: withConstrApps ()
3581+ if isSimpleLiteral then rejectWildcardType(annotType() ) :: Nil
3582+ else constrApps ()
35413583 val parentsIsType = parents.length == 1 && parents.head.isType
3584+ newLineOptWhenFollowedBy(LBRACE )
35423585 if in.token == EQUALS && parentsIsType then
35433586 accept(EQUALS )
35443587 mods1 |= Final
@@ -3547,17 +3590,17 @@ object Parsers {
35473590 ValDef (name, parents.head, subExpr())
35483591 else
35493592 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
35553594 val tparams1 = tparams.map(tparam => tparam.withMods(tparam.mods | PrivateLocal ))
35563595 val vparamss1 = vparamss.map(_.map(vparam =>
35573596 vparam.withMods(vparam.mods &~ Param | ParamAccessor | Protected )))
3558- val templ = withTemplate (makeConstructor(tparams1, vparamss1), parents)
3597+ val templ = templateBodyOpt (makeConstructor(tparams1, vparamss1), parents, Nil )
35593598 if noParams then ModuleDef (name, templ)
35603599 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 )
35613604 end gdef
35623605 finalizeDef(gdef, mods1, start)
35633606 }
@@ -3576,8 +3619,11 @@ object Parsers {
35763619 isUsingClause(extParams)
35773620 do ()
35783621 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. """ )
35813627 in.nextToken()
35823628 val methods =
35833629 if isDefIntro(modifierTokens) then
@@ -3626,25 +3672,24 @@ object Parsers {
36263672 // Using Ident(tpnme.ERROR) to avoid causing cascade errors on non-user-written code
36273673 if in.token == LPAREN then parArgumentExprss(wrapNew(t)) else t
36283674
3629- /** ConstrApps ::= ConstrApp {( ‘,’ | ‘with’) ConstrApp}
3675+ /** ConstrApps ::= ConstrApp ({ ‘,’ ConstrApp} | { ‘with’ ConstrApp})
36303676 */
3631- def constrApps (commaOK : Boolean ): List [Tree ] =
3677+ def constrApps (): List [Tree ] =
36323678 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()
36383680 t :: ts
36393681
3682+ /** `{`,` ConstrApp} */
3683+ def commaConstrApps (): List [Tree ] =
3684+ if in.token == COMMA then
3685+ in.nextToken()
3686+ constrApp() :: commaConstrApps()
3687+ else Nil
36403688
36413689 /** `{`with` ConstrApp} but no EOL allowed after `with`.
36423690 */
36433691 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
36483693 in.nextToken()
36493694 constrApp() :: withConstrApps()
36503695 else Nil
@@ -3662,7 +3707,7 @@ object Parsers {
36623707 in.sourcePos())
36633708 Nil
36643709 }
3665- else constrApps(commaOK = true )
3710+ else constrApps()
36663711 }
36673712 else Nil
36683713 newLinesOptWhenFollowedBy(nme.derives )
@@ -3688,39 +3733,65 @@ object Parsers {
36883733 template(constr)
36893734 else
36903735 possibleTemplateStart()
3691- if in.isNestedStart then
3736+ if isTemplateBodyStart then
36923737 template(constr)
36933738 else
36943739 checkNextNotIndented()
36953740 Template (constr, Nil , Nil , EmptyValDef , Nil )
36963741
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
36993746 */
37003747 def templateBodyOpt (constr : DefDef , parents : List [Tree ], derived : List [Tree ]): Template =
37013748 val (self, stats) =
3702- if in.isNestedStart then
3749+ if isTemplateBodyStart then
37033750 templateBody()
37043751 else
37053752 checkNextNotIndented()
37063753 (EmptyValDef , Nil )
37073754 Template (constr, parents, derived, self, stats)
37083755
37093756 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 )
37113763 if in.token == WITH then
37123764 syntaxError(EarlyDefinitionsNotSupported ())
37133765 in.nextToken()
37143766 template(emptyConstructor)
37153767 r
37163768
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+ }
37243795
37253796/* -------- STATSEQS ------------------------------------------- */
37263797
@@ -3787,10 +3858,10 @@ object Parsers {
37873858 * EnumStat ::= TemplateStat
37883859 * | Annotations Modifiers EnumCase
37893860 */
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
37923863 val stats = new ListBuffer [Tree ]
3793- if (isExprIntro && ! isDefIntro(modifierTokens)) {
3864+ if (self.isEmpty && isExprIntro && ! isDefIntro(modifierTokens)) {
37943865 val first = expr1()
37953866 if (in.token == ARROW ) {
37963867 first match {
@@ -3801,12 +3872,20 @@ object Parsers {
38013872 if (name != nme.ERROR )
38023873 self = makeSelfDef(name, tpt).withSpan(first.span)
38033874 }
3804- in.token = SELFARROW // suppresses INDENT insertion after `=>`
3805- in.nextToken()
3875+ in.nextTokenNoIndent()
38063876 }
38073877 else {
38083878 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)
38103889 }
38113890 }
38123891 var exitOnError = false
@@ -3934,7 +4013,7 @@ object Parsers {
39344013 possibleTemplateStart()
39354014 if in.token == EOF then
39364015 ts += makePackaging(start, pkg, List ())
3937- else if in.isNestedStart then
4016+ else if isTemplateBodyStart then
39384017 ts += inDefScopeBraces(makePackaging(start, pkg, topStatSeq()), rewriteWithColon = true )
39394018 continue = true
39404019 else
@@ -3972,6 +4051,7 @@ object Parsers {
39724051 }
39734052
39744053 override def templateBody (): (ValDef , List [Thicket ]) = {
4054+ if in.token == WITH then in.nextToken()
39754055 skipBraces()
39764056 (EmptyValDef , List (EmptyTree ))
39774057 }
0 commit comments