@@ -26,6 +26,7 @@ import Decorators._
2626import scala .internal .Chars
2727import scala .annotation .{tailrec , switch }
2828import rewrites .Rewrites .{patch , overlapsPatch }
29+ import config .Config .silentTemplateIndent
2930
3031object Parsers {
3132
@@ -124,13 +125,13 @@ object Parsers {
124125
125126 /* ------------- ERROR HANDLING ------------------------------------------- */
126127 /** The offset where the last syntax error was reported, or if a skip to a
127- * safepoint occurred afterwards, the offset of the safe point.
128- */
128+ * safepoint occurred afterwards, the offset of the safe point.
129+ */
129130 protected var lastErrorOffset : Int = - 1
130131
131132 /** Issue an error at given offset if beyond last error offset
132- * and update lastErrorOffset.
133- */
133+ * and update lastErrorOffset.
134+ */
134135 def syntaxError (msg : => Message , offset : Int = in.offset): Unit =
135136 if (offset > lastErrorOffset) {
136137 val length = if (offset == in.offset && in.name != null ) in.name.show.length else 0
@@ -337,6 +338,9 @@ object Parsers {
337338 offset
338339 }
339340
341+ def reportMissing (expected : Token ): Unit =
342+ syntaxError(ExpectedTokenButFound (expected, in.token))
343+
340344 /** semi = nl {nl} | `;'
341345 * nl = `\n' // where allowed
342346 */
@@ -356,6 +360,17 @@ object Parsers {
356360 accept(SEMI )
357361 }
358362
363+ /** Under -language:Scala2 or -old-syntax, flag
364+ *
365+ * extends p1 with new p1 with t1 with
366+ * p2 p2 t2
367+ *
368+ * as a migration warning or error since that means something else under significant indentation.
369+ */
370+ def checkNotWithAtEOL (): Unit =
371+ if (in.isScala2Mode || in.oldSyntax) && in.isAfterLineEnd then
372+ in.errorOrMigrationWarning(" `with` cannot be followed by new line, place at beginning of next line instead" )
373+
359374 def rewriteNotice (additionalOption : String = " " ) = {
360375 val optionStr = if (additionalOption.isEmpty) " " else " " ++ additionalOption
361376 i " \n This construct can be rewritten automatically under $optionStr -rewrite. "
@@ -616,6 +631,7 @@ object Parsers {
616631
617632 /** If indentation is not significant, check that this is not the start of a
618633 * statement that's indented relative to the current region.
634+ * TODO: Drop if `with` is required before indented template definitions.
619635 */
620636 def checkNextNotIndented (): Unit = in.currentRegion match
621637 case r : IndentSignificantRegion if in.isNewLine =>
@@ -1249,10 +1265,14 @@ object Parsers {
12491265 newLineOptWhenFollowedBy(LBRACE )
12501266 }
12511267
1252- def possibleTemplateStart (): Unit = {
1253- in.observeIndented()
1254- newLineOptWhenFollowedBy(LBRACE )
1255- }
1268+ def possibleTemplateStart (isNew : Boolean = false ): Unit =
1269+ if in.token == WITH then
1270+ in.nextToken()
1271+ if in.token != LBRACE && in.token != INDENT then
1272+ syntaxError(i " indented definitions or `{' expected " )
1273+ else
1274+ if silentTemplateIndent && ! isNew then in.observeIndented()
1275+ newLineOptWhenFollowedBy(LBRACE )
12561276
12571277 def indentRegion [T ](tag : EndMarkerTag )(op : => T ): T = {
12581278 val iw = in.currentRegion.indentWidth
@@ -1396,7 +1416,7 @@ object Parsers {
13961416 makeParameter(name, typ(), mods | Param )
13971417 }
13981418
1399- /** InfixType ::= RefinedType {id [nl] refinedType }
1419+ /** InfixType ::= RefinedType {id [nl] RefinedType }
14001420 */
14011421 def infixType (): Tree = infixTypeRest(refinedType())
14021422
@@ -1407,7 +1427,7 @@ object Parsers {
14071427 def infixTypeRest (t : Tree ): Tree =
14081428 infixOps(t, canStartTypeTokens, refinedType, isType = true , isOperator = ! isPostfixStar)
14091429
1410- /** RefinedType ::= WithType {Annotation | [nl ] Refinement}
1430+ /** RefinedType ::= WithType {[nl | `with' ] Refinement}
14111431 */
14121432 val refinedType : () => Tree = () => refinedTypeRest(withType())
14131433
@@ -1423,12 +1443,16 @@ object Parsers {
14231443 def withType (): Tree = withTypeRest(annotType())
14241444
14251445 def withTypeRest (t : Tree ): Tree =
1426- if (in.token == WITH ) {
1427- if (ctx.settings.strict.value)
1428- deprecationWarning(DeprecatedWithOperator ())
1446+ if in.token == WITH then
1447+ val withOffset = in.offset
14291448 in.nextToken()
1430- makeAndType(t, withType())
1431- }
1449+ if in.token == LBRACE || in.token == INDENT then
1450+ t
1451+ else
1452+ checkNotWithAtEOL()
1453+ if (ctx.settings.strict.value)
1454+ deprecationWarning(DeprecatedWithOperator (), withOffset)
1455+ makeAndType(t, withType())
14321456 else t
14331457
14341458 /** AnnotType ::= SimpleType {Annotation}
@@ -1676,7 +1700,7 @@ object Parsers {
16761700 else
16771701 if (altToken == THEN || enclosedInParens) && in.isNewLine then
16781702 in.observeIndented()
1679- if ! enclosedInParens && in.token != INDENT then accept (altToken)
1703+ if ! enclosedInParens && in.token != INDENT then reportMissing (altToken)
16801704 if (rewriteToNewSyntax(t.span))
16811705 dropParensOrBraces(t.span.start, s " ${tokenString(altToken)}" )
16821706 t
@@ -2125,26 +2149,19 @@ object Parsers {
21252149 }
21262150 }
21272151
2128- /** SimpleExpr ::= ‘new’ (ConstrApp {`with` ConstrApp} [TemplateBody] | TemplateBody)
2152+ /** SimpleExpr ::= ‘new’ ConstrApp {`with` ConstrApp} [TemplateBody]
2153+ * | ‘new’ TemplateBody
21292154 */
21302155 def newExpr (): Tree =
21312156 indentRegion(NEW ) {
21322157 val start = in.skipToken()
21332158 def reposition (t : Tree ) = t.withSpan(Span (start, in.lastOffset))
21342159 possibleBracesStart()
21352160 val parents =
2136- if (in.isNestedStart) Nil
2137- else constrApp() :: {
2138- if (in.token == WITH ) {
2139- // Enable this for 3.1, when we drop `with` for inheritance:
2140- // in.errorUnlessInScala2Mode(
2141- // "anonymous class with multiple parents is no longer supported; use a named class instead")
2142- in.nextToken()
2143- tokenSeparated(WITH , constrApp)
2144- }
2145- else Nil
2146- }
2147- possibleBracesStart()
2161+ if in.token == LBRACE || in.token == WITH then Nil
2162+ else constrApps(commaOK = false , templateCanFollow = true )
2163+ colonAtEOLOpt()
2164+ possibleTemplateStart(isNew = true )
21482165 parents match {
21492166 case parent :: Nil if ! in.isNestedStart =>
21502167 reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent)
@@ -3331,7 +3348,7 @@ object Parsers {
33313348 val parents =
33323349 if (in.token == EXTENDS ) {
33333350 in.nextToken()
3334- tokenSeparated( WITH , constrApp )
3351+ constrApps(commaOK = true , templateCanFollow = false )
33353352 }
33363353 else Nil
33373354 Template (constr, parents, Nil , EmptyValDef , Nil )
@@ -3347,12 +3364,20 @@ object Parsers {
33473364 case _ =>
33483365 syntaxError(em " extension clause must start with a single regular parameter " , start)
33493366
3367+ def checkExtensionMethod (stat : Tree ): Unit = stat match {
3368+ case stat : DefDef =>
3369+ if stat.mods.is(Extension ) then
3370+ syntaxError(i " no extension method allowed here since leading parameter was already given " , stat.span)
3371+ case _ =>
3372+ syntaxError(i " extension clause can only define methods " , stat.span)
3373+ }
33503374
33513375 /** GivenDef ::= [GivenSig (‘:’ | <:)] Type ‘=’ Expr
33523376 * | [GivenSig ‘:’] [ConstrApp {‘,’ ConstrApp }] [TemplateBody]
3353- * | [id ‘:’] [ ExtParamClause] TemplateBody
3377+ * | [id ‘:’] ExtParamClause ExtMethods
33543378 * GivenSig ::= [id] [DefTypeParamClause] {GivenParamClause}
33553379 * ExtParamClause ::= [DefTypeParamClause] DefParamClause {GivenParamClause}
3380+ * ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
33563381 */
33573382 def givenDef (start : Offset , mods : Modifiers , instanceMod : Mod ) = atSpan(start, nameStart) {
33583383 var mods1 = addMod(mods, instanceMod)
@@ -3381,6 +3406,7 @@ object Parsers {
33813406 if in.token == COLON then
33823407 in.nextToken()
33833408 if in.token == LBRACE
3409+ || in.token == WITH
33843410 || in.token == LBRACKET
33853411 || in.token == LPAREN && followingIsParamOrGivenType()
33863412 then
@@ -3393,8 +3419,10 @@ object Parsers {
33933419 syntaxError(" `<:' is only allowed for given with `inline' modifier" )
33943420 in.nextToken()
33953421 TypeBoundsTree (EmptyTree , toplevelTyp()) :: Nil
3396- else if name.isEmpty && in.token != LBRACE then
3397- tokenSeparated(COMMA , constrApp)
3422+ else if name.isEmpty
3423+ && in.token != LBRACE && in.token != WITH
3424+ && ! hasExtensionParams
3425+ then tokenSeparated(COMMA , constrApp)
33983426 else Nil
33993427
34003428 val gdef =
@@ -3407,12 +3435,17 @@ object Parsers {
34073435 case TypeBoundsTree (_, _) :: _ => syntaxError(" `=' expected" )
34083436 case _ =>
34093437 possibleTemplateStart()
3410- if ! hasExtensionParams then
3438+ if hasExtensionParams then
3439+ in.observeIndented()
3440+ else
34113441 tparams = tparams.map(tparam => tparam.withMods(tparam.mods | PrivateLocal ))
34123442 vparamss = vparamss.map(_.map(vparam =>
34133443 vparam.withMods(vparam.mods &~ Param | ParamAccessor | PrivateLocal )))
34143444 val templ = templateBodyOpt(makeConstructor(tparams, vparamss), parents, Nil )
3415- if tparams.isEmpty && vparamss.isEmpty || hasExtensionParams then ModuleDef (name, templ)
3445+ if hasExtensionParams then
3446+ templ.body.foreach(checkExtensionMethod)
3447+ ModuleDef (name, templ)
3448+ else if tparams.isEmpty && vparamss.isEmpty then ModuleDef (name, templ)
34163449 else TypeDef (name.toTypeName, templ)
34173450
34183451 finalizeDef(gdef, mods1, start)
@@ -3429,51 +3462,47 @@ object Parsers {
34293462 if in.token == LPAREN then parArgumentExprss(wrapNew(t)) else t
34303463 }
34313464
3432- /** ConstrApps ::= ConstrApp {‘with’ ConstrApp} (to be deprecated in 3.1)
3433- * | ConstrApp {‘,’ ConstrApp}
3465+ /** ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp}
34343466 */
3435- def constrApps (): List [Tree ] = {
3467+ def constrApps (commaOK : Boolean , templateCanFollow : Boolean ): List [Tree ] =
34363468 val t = constrApp()
34373469 val ts =
3438- if (in.token == WITH ) {
3439- in.nextToken()
3440- tokenSeparated(WITH , constrApp)
3441- }
3442- else if (in.token == COMMA ) {
3470+ if in.token == WITH then
3471+ val lookahead = in.LookaheadScanner (indent = true )
3472+ lookahead.nextToken()
3473+ if templateCanFollow && (lookahead.token == LBRACE || lookahead.token == INDENT ) then
3474+ Nil
3475+ else
3476+ in.nextToken()
3477+ checkNotWithAtEOL()
3478+ constrApps(commaOK, templateCanFollow)
3479+ else if commaOK && in.token == COMMA then
34433480 in.nextToken()
3444- tokenSeparated(COMMA , constrApp)
3445- }
3481+ constrApps(commaOK, templateCanFollow)
34463482 else Nil
34473483 t :: ts
3448- }
34493484
3450- /** InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]
3485+ /** Template ::= InheritClauses [TemplateBody]
3486+ * InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]
34513487 */
3452- def inheritClauses () : ( List [ Tree ], List [ Tree ]) = {
3453- val extended =
3488+ def template ( constr : DefDef , isEnum : Boolean = false ) : Template = {
3489+ val parents =
34543490 if (in.token == EXTENDS ) {
34553491 in.nextToken()
34563492 if (in.token == LBRACE || in.token == COLONEOL ) {
34573493 in.errorOrMigrationWarning(" `extends' must be followed by at least one parent" )
34583494 Nil
34593495 }
3460- else constrApps()
3496+ else constrApps(commaOK = true , templateCanFollow = true )
34613497 }
34623498 else Nil
3499+ newLinesOptWhenFollowedBy(nme.derives )
34633500 val derived =
34643501 if (isIdent(nme.derives )) {
34653502 in.nextToken()
34663503 tokenSeparated(COMMA , () => convertToTypeId(qualId()))
34673504 }
34683505 else Nil
3469- (extended, derived)
3470- }
3471-
3472- /** Template ::= InheritClauses [TemplateBody]
3473- */
3474- def template (constr : DefDef , isEnum : Boolean = false ): Template = {
3475- newLinesOptWhenFollowedBy(nme.derives )
3476- val (parents, derived) = inheritClauses()
34773506 possibleTemplateStart()
34783507 if (isEnum) {
34793508 val (self, stats) = withinEnum(templateBody())
@@ -3496,7 +3525,8 @@ object Parsers {
34963525 checkNextNotIndented()
34973526 Template (constr, Nil , Nil , EmptyValDef , Nil )
34983527
3499- /** TemplateBody ::= [nl] `{' TemplateStatSeq `}'
3528+ /** TemplateBody ::= [nl | `with'] `{' TemplateStatSeq `}'
3529+ * EnumBody ::= [nl | ‘with’] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’
35003530 */
35013531 def templateBodyOpt (constr : DefDef , parents : List [Tree ], derived : List [Tree ]): Template =
35023532 val (self, stats) =
@@ -3524,7 +3554,7 @@ object Parsers {
35243554 case x : RefTree => atSpan(start, pointOffset(pkg))(PackageDef (x, stats))
35253555 }
35263556
3527- /** Packaging ::= package QualId [nl] `{' TopStatSeq `}'
3557+ /** Packaging ::= package QualId [nl | `with' ] `{' TopStatSeq `}'
35283558 */
35293559 def packaging (start : Int ): Tree = {
35303560 val pkg = qualId()
@@ -3713,23 +3743,23 @@ object Parsers {
37133743 ts ++= topStatSeq()
37143744 }
37153745 }
3716- else {
3746+ else
37173747 val pkg = qualId()
3748+ var continue = false
37183749 indentRegion(pkg) {
37193750 possibleTemplateStart()
3720- if ( in.token == EOF )
3751+ if in.token == EOF then
37213752 ts += makePackaging(start, pkg, List ())
3722- else if ( in.isNestedStart) {
3753+ else if in.isNestedStart then
37233754 ts += inDefScopeBraces(makePackaging(start, pkg, topStatSeq()))
3724- acceptStatSepUnlessAtEnd()
3725- ts ++= topStatSeq()
3726- }
3727- else {
3755+ continue = true
3756+ else
37283757 acceptStatSep()
37293758 ts += makePackaging(start, pkg, topstats())
3730- }
37313759 }
3732- }
3760+ if continue then
3761+ acceptStatSepUnlessAtEnd()
3762+ ts ++= topStatSeq()
37333763 }
37343764 else
37353765 ts ++= topStatSeq()
0 commit comments