@@ -26,6 +26,7 @@ import Decorators._
26
26
import scala .internal .Chars
27
27
import scala .annotation .{tailrec , switch }
28
28
import rewrites .Rewrites .{patch , overlapsPatch }
29
+ import config .Config .silentTemplateIndent
29
30
30
31
object Parsers {
31
32
@@ -124,13 +125,13 @@ object Parsers {
124
125
125
126
/* ------------- ERROR HANDLING ------------------------------------------- */
126
127
/** 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
+ */
129
130
protected var lastErrorOffset : Int = - 1
130
131
131
132
/** Issue an error at given offset if beyond last error offset
132
- * and update lastErrorOffset.
133
- */
133
+ * and update lastErrorOffset.
134
+ */
134
135
def syntaxError (msg : => Message , offset : Int = in.offset): Unit =
135
136
if (offset > lastErrorOffset) {
136
137
val length = if (offset == in.offset && in.name != null ) in.name.show.length else 0
@@ -337,6 +338,9 @@ object Parsers {
337
338
offset
338
339
}
339
340
341
+ def reportMissing (expected : Token ): Unit =
342
+ syntaxError(ExpectedTokenButFound (expected, in.token))
343
+
340
344
/** semi = nl {nl} | `;'
341
345
* nl = `\n' // where allowed
342
346
*/
@@ -356,6 +360,17 @@ object Parsers {
356
360
accept(SEMI )
357
361
}
358
362
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
+
359
374
def rewriteNotice (additionalOption : String = " " ) = {
360
375
val optionStr = if (additionalOption.isEmpty) " " else " " ++ additionalOption
361
376
i " \n This construct can be rewritten automatically under $optionStr -rewrite. "
@@ -616,6 +631,7 @@ object Parsers {
616
631
617
632
/** If indentation is not significant, check that this is not the start of a
618
633
* statement that's indented relative to the current region.
634
+ * TODO: Drop if `with` is required before indented template definitions.
619
635
*/
620
636
def checkNextNotIndented (): Unit = in.currentRegion match
621
637
case r : IndentSignificantRegion if in.isNewLine =>
@@ -1249,10 +1265,14 @@ object Parsers {
1249
1265
newLineOptWhenFollowedBy(LBRACE )
1250
1266
}
1251
1267
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 )
1256
1276
1257
1277
def indentRegion [T ](tag : EndMarkerTag )(op : => T ): T = {
1258
1278
val iw = in.currentRegion.indentWidth
@@ -1396,7 +1416,7 @@ object Parsers {
1396
1416
makeParameter(name, typ(), mods | Param )
1397
1417
}
1398
1418
1399
- /** InfixType ::= RefinedType {id [nl] refinedType }
1419
+ /** InfixType ::= RefinedType {id [nl] RefinedType }
1400
1420
*/
1401
1421
def infixType (): Tree = infixTypeRest(refinedType())
1402
1422
@@ -1407,7 +1427,7 @@ object Parsers {
1407
1427
def infixTypeRest (t : Tree ): Tree =
1408
1428
infixOps(t, canStartTypeTokens, refinedType, isType = true , isOperator = ! isPostfixStar)
1409
1429
1410
- /** RefinedType ::= WithType {Annotation | [nl ] Refinement}
1430
+ /** RefinedType ::= WithType {[nl | `with' ] Refinement}
1411
1431
*/
1412
1432
val refinedType : () => Tree = () => refinedTypeRest(withType())
1413
1433
@@ -1423,12 +1443,16 @@ object Parsers {
1423
1443
def withType (): Tree = withTypeRest(annotType())
1424
1444
1425
1445
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
1429
1448
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())
1432
1456
else t
1433
1457
1434
1458
/** AnnotType ::= SimpleType {Annotation}
@@ -1676,7 +1700,7 @@ object Parsers {
1676
1700
else
1677
1701
if (altToken == THEN || enclosedInParens) && in.isNewLine then
1678
1702
in.observeIndented()
1679
- if ! enclosedInParens && in.token != INDENT then accept (altToken)
1703
+ if ! enclosedInParens && in.token != INDENT then reportMissing (altToken)
1680
1704
if (rewriteToNewSyntax(t.span))
1681
1705
dropParensOrBraces(t.span.start, s " ${tokenString(altToken)}" )
1682
1706
t
@@ -2125,26 +2149,19 @@ object Parsers {
2125
2149
}
2126
2150
}
2127
2151
2128
- /** SimpleExpr ::= ‘new’ (ConstrApp {`with` ConstrApp} [TemplateBody] | TemplateBody)
2152
+ /** SimpleExpr ::= ‘new’ ConstrApp {`with` ConstrApp} [TemplateBody]
2153
+ * | ‘new’ TemplateBody
2129
2154
*/
2130
2155
def newExpr (): Tree =
2131
2156
indentRegion(NEW ) {
2132
2157
val start = in.skipToken()
2133
2158
def reposition (t : Tree ) = t.withSpan(Span (start, in.lastOffset))
2134
2159
possibleBracesStart()
2135
2160
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 )
2148
2165
parents match {
2149
2166
case parent :: Nil if ! in.isNestedStart =>
2150
2167
reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent)
@@ -3331,7 +3348,7 @@ object Parsers {
3331
3348
val parents =
3332
3349
if (in.token == EXTENDS ) {
3333
3350
in.nextToken()
3334
- tokenSeparated( WITH , constrApp )
3351
+ constrApps(commaOK = true , templateCanFollow = false )
3335
3352
}
3336
3353
else Nil
3337
3354
Template (constr, parents, Nil , EmptyValDef , Nil )
@@ -3347,12 +3364,20 @@ object Parsers {
3347
3364
case _ =>
3348
3365
syntaxError(em " extension clause must start with a single regular parameter " , start)
3349
3366
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
+ }
3350
3374
3351
3375
/** GivenDef ::= [GivenSig (‘:’ | <:)] Type ‘=’ Expr
3352
3376
* | [GivenSig ‘:’] [ConstrApp {‘,’ ConstrApp }] [TemplateBody]
3353
- * | [id ‘:’] [ ExtParamClause] TemplateBody
3377
+ * | [id ‘:’] ExtParamClause ExtMethods
3354
3378
* GivenSig ::= [id] [DefTypeParamClause] {GivenParamClause}
3355
3379
* ExtParamClause ::= [DefTypeParamClause] DefParamClause {GivenParamClause}
3380
+ * ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
3356
3381
*/
3357
3382
def givenDef (start : Offset , mods : Modifiers , instanceMod : Mod ) = atSpan(start, nameStart) {
3358
3383
var mods1 = addMod(mods, instanceMod)
@@ -3381,6 +3406,7 @@ object Parsers {
3381
3406
if in.token == COLON then
3382
3407
in.nextToken()
3383
3408
if in.token == LBRACE
3409
+ || in.token == WITH
3384
3410
|| in.token == LBRACKET
3385
3411
|| in.token == LPAREN && followingIsParamOrGivenType()
3386
3412
then
@@ -3393,8 +3419,10 @@ object Parsers {
3393
3419
syntaxError(" `<:' is only allowed for given with `inline' modifier" )
3394
3420
in.nextToken()
3395
3421
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)
3398
3426
else Nil
3399
3427
3400
3428
val gdef =
@@ -3407,12 +3435,17 @@ object Parsers {
3407
3435
case TypeBoundsTree (_, _) :: _ => syntaxError(" `=' expected" )
3408
3436
case _ =>
3409
3437
possibleTemplateStart()
3410
- if ! hasExtensionParams then
3438
+ if hasExtensionParams then
3439
+ in.observeIndented()
3440
+ else
3411
3441
tparams = tparams.map(tparam => tparam.withMods(tparam.mods | PrivateLocal ))
3412
3442
vparamss = vparamss.map(_.map(vparam =>
3413
3443
vparam.withMods(vparam.mods &~ Param | ParamAccessor | PrivateLocal )))
3414
3444
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)
3416
3449
else TypeDef (name.toTypeName, templ)
3417
3450
3418
3451
finalizeDef(gdef, mods1, start)
@@ -3429,51 +3462,47 @@ object Parsers {
3429
3462
if in.token == LPAREN then parArgumentExprss(wrapNew(t)) else t
3430
3463
}
3431
3464
3432
- /** ConstrApps ::= ConstrApp {‘with’ ConstrApp} (to be deprecated in 3.1)
3433
- * | ConstrApp {‘,’ ConstrApp}
3465
+ /** ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp}
3434
3466
*/
3435
- def constrApps (): List [Tree ] = {
3467
+ def constrApps (commaOK : Boolean , templateCanFollow : Boolean ): List [Tree ] =
3436
3468
val t = constrApp()
3437
3469
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
3443
3480
in.nextToken()
3444
- tokenSeparated(COMMA , constrApp)
3445
- }
3481
+ constrApps(commaOK, templateCanFollow)
3446
3482
else Nil
3447
3483
t :: ts
3448
- }
3449
3484
3450
- /** InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]
3485
+ /** Template ::= InheritClauses [TemplateBody]
3486
+ * InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]
3451
3487
*/
3452
- def inheritClauses () : ( List [ Tree ], List [ Tree ]) = {
3453
- val extended =
3488
+ def template ( constr : DefDef , isEnum : Boolean = false ) : Template = {
3489
+ val parents =
3454
3490
if (in.token == EXTENDS ) {
3455
3491
in.nextToken()
3456
3492
if (in.token == LBRACE || in.token == COLONEOL ) {
3457
3493
in.errorOrMigrationWarning(" `extends' must be followed by at least one parent" )
3458
3494
Nil
3459
3495
}
3460
- else constrApps()
3496
+ else constrApps(commaOK = true , templateCanFollow = true )
3461
3497
}
3462
3498
else Nil
3499
+ newLinesOptWhenFollowedBy(nme.derives )
3463
3500
val derived =
3464
3501
if (isIdent(nme.derives )) {
3465
3502
in.nextToken()
3466
3503
tokenSeparated(COMMA , () => convertToTypeId(qualId()))
3467
3504
}
3468
3505
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()
3477
3506
possibleTemplateStart()
3478
3507
if (isEnum) {
3479
3508
val (self, stats) = withinEnum(templateBody())
@@ -3496,7 +3525,8 @@ object Parsers {
3496
3525
checkNextNotIndented()
3497
3526
Template (constr, Nil , Nil , EmptyValDef , Nil )
3498
3527
3499
- /** TemplateBody ::= [nl] `{' TemplateStatSeq `}'
3528
+ /** TemplateBody ::= [nl | `with'] `{' TemplateStatSeq `}'
3529
+ * EnumBody ::= [nl | ‘with’] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’
3500
3530
*/
3501
3531
def templateBodyOpt (constr : DefDef , parents : List [Tree ], derived : List [Tree ]): Template =
3502
3532
val (self, stats) =
@@ -3524,7 +3554,7 @@ object Parsers {
3524
3554
case x : RefTree => atSpan(start, pointOffset(pkg))(PackageDef (x, stats))
3525
3555
}
3526
3556
3527
- /** Packaging ::= package QualId [nl] `{' TopStatSeq `}'
3557
+ /** Packaging ::= package QualId [nl | `with' ] `{' TopStatSeq `}'
3528
3558
*/
3529
3559
def packaging (start : Int ): Tree = {
3530
3560
val pkg = qualId()
@@ -3713,23 +3743,23 @@ object Parsers {
3713
3743
ts ++= topStatSeq()
3714
3744
}
3715
3745
}
3716
- else {
3746
+ else
3717
3747
val pkg = qualId()
3748
+ var continue = false
3718
3749
indentRegion(pkg) {
3719
3750
possibleTemplateStart()
3720
- if ( in.token == EOF )
3751
+ if in.token == EOF then
3721
3752
ts += makePackaging(start, pkg, List ())
3722
- else if ( in.isNestedStart) {
3753
+ else if in.isNestedStart then
3723
3754
ts += inDefScopeBraces(makePackaging(start, pkg, topStatSeq()))
3724
- acceptStatSepUnlessAtEnd()
3725
- ts ++= topStatSeq()
3726
- }
3727
- else {
3755
+ continue = true
3756
+ else
3728
3757
acceptStatSep()
3729
3758
ts += makePackaging(start, pkg, topstats())
3730
- }
3731
3759
}
3732
- }
3760
+ if continue then
3761
+ acceptStatSepUnlessAtEnd()
3762
+ ts ++= topStatSeq()
3733
3763
}
3734
3764
else
3735
3765
ts ++= topStatSeq()
0 commit comments