@@ -294,6 +294,41 @@ object Parsers {
294
294
} finally inFunReturnType = saved
295
295
}
296
296
297
+ /** A placeholder for dummy arguments that should be re-parsed as parameters */
298
+ val ParamNotArg = EmptyTree
299
+
300
+ /** A flag indicating we are parsing in the annotations of a primary
301
+ * class constructor
302
+ */
303
+ private var inClassConstrAnnots = false
304
+
305
+ private def fromWithinClassConstr [T ](body : => T ): T = {
306
+ val saved = inClassConstrAnnots
307
+ try {
308
+ inClassConstrAnnots = true
309
+ body
310
+ } finally {
311
+ inClassConstrAnnots = saved
312
+ if (lookaheadTokens.nonEmpty) {
313
+ in.insertTokens(lookaheadTokens.toList)
314
+ lookaheadTokens.clear()
315
+ }
316
+ }
317
+ }
318
+
319
+ /** Lookahead tokens for the case of annotations in class constructors.
320
+ * We store tokens in lookahead as long as they can form a valid prefix
321
+ * of a class parameter clause.
322
+ */
323
+ private var lookaheadTokens = new ListBuffer [TokenData ]
324
+
325
+ /** Copy current token to end of lookahead */
326
+ private def saveLookahead () = {
327
+ val lookahead = new TokenData {}
328
+ lookahead.copyFrom(in)
329
+ lookaheadTokens += lookahead
330
+ }
331
+
297
332
def migrationWarningOrError (msg : String , offset : Int = in.offset) =
298
333
if (in.isScala2Mode)
299
334
ctx.migrationWarning(msg, source atPos Position (offset))
@@ -1280,20 +1315,77 @@ object Parsers {
1280
1315
if (in.token == RPAREN ) Nil else commaSeparated(exprInParens)
1281
1316
1282
1317
/** ParArgumentExprs ::= `(' [ExprsInParens] `)'
1283
- * | `(' [ExprsInParens `,'] PostfixExpr `:' `_' `*' ')' \
1284
- */
1285
- def parArgumentExprs (): List [Tree ] =
1286
- inParens(if (in.token == RPAREN ) Nil else commaSeparated(argumentExpr))
1318
+ * | `(' [ExprsInParens `,'] PostfixExpr `:' `_' `*' ')'
1319
+ *
1320
+ * Special treatment for arguments of primary class constructor
1321
+ * annotations. Empty argument lists `(` `)` get represented
1322
+ * as `List(ParamNotArg)` instead of `Nil`, indicating that the
1323
+ * token sequence should be interpreted as an empty parameter clause
1324
+ * instead. `ParamNotArg` can also be produced when parsing the first
1325
+ * argument (see `classConstrAnnotExpr`).
1326
+ *
1327
+ * The method affects `lookaheadTokens` as a side effect.
1328
+ * If the argument list parses as `List(ParamNotArg)`, `lookaheadTokens`
1329
+ * contains the tokens that need to be replayed to parse the parameter clause.
1330
+ * Otherwise, `lookaheadTokens` is empty.
1331
+ */
1332
+ def parArgumentExprs (): List [Tree ] = {
1333
+ if (inClassConstrAnnots) {
1334
+ assert(lookaheadTokens.isEmpty)
1335
+ saveLookahead()
1336
+ accept(LPAREN )
1337
+ val args =
1338
+ if (in.token == RPAREN ) ParamNotArg :: Nil
1339
+ else {
1340
+ openParens.change(LPAREN , + 1 )
1341
+ try commaSeparated(argumentExpr)
1342
+ finally openParens.change(LPAREN , - 1 )
1343
+ }
1344
+ if (args == ParamNotArg :: Nil ) in.adjustSepRegions(RPAREN ) // simulate `)` without requiring it
1345
+ else {
1346
+ lookaheadTokens.clear()
1347
+ accept(RPAREN )
1348
+ }
1349
+ args
1350
+ }
1351
+ else
1352
+ inParens(if (in.token == RPAREN ) Nil else commaSeparated(argumentExpr))
1353
+
1354
+ }
1287
1355
1288
1356
/** ArgumentExprs ::= ParArgumentExprs
1289
1357
* | [nl] BlockExpr
1290
1358
*/
1291
1359
def argumentExprs (): List [Tree ] =
1292
1360
if (in.token == LBRACE ) blockExpr() :: Nil else parArgumentExprs()
1293
1361
1294
- val argumentExpr = () => exprInParens() match {
1295
- case a @ Assign (Ident (id), rhs) => cpy.NamedArg (a)(id, rhs)
1296
- case e => e
1362
+ val argumentExpr = () => {
1363
+ val arg =
1364
+ if (inClassConstrAnnots && lookaheadTokens.nonEmpty) classConstrAnnotExpr()
1365
+ else exprInParens()
1366
+ arg match {
1367
+ case arg @ Assign (Ident (id), rhs) => cpy.NamedArg (arg)(id, rhs)
1368
+ case arg => arg
1369
+ }
1370
+ }
1371
+
1372
+ /** Handle first argument of an argument list to an annotation of
1373
+ * a primary class constructor. If the current token either cannot
1374
+ * start an expression or is an identifier and is followed by `:`,
1375
+ * stop parsing the rest of the expression and return `EmptyTree`,
1376
+ * indicating that we should re-parse the expression as a parameter clause.
1377
+ * Otherwise clear the lookahead buffer and parse as normal.
1378
+ */
1379
+ def classConstrAnnotExpr () = {
1380
+ saveLookahead()
1381
+ if (in.token == IDENTIFIER ) {
1382
+ postfixExpr() match {
1383
+ case Ident (_) if in.token == COLON => ParamNotArg
1384
+ case t => expr1Rest(t, Location .InParens )
1385
+ }
1386
+ }
1387
+ else if (isExprIntro) exprInParens()
1388
+ else ParamNotArg
1297
1389
}
1298
1390
1299
1391
/** ArgumentExprss ::= {ArgumentExprs}
@@ -1305,9 +1397,17 @@ object Parsers {
1305
1397
}
1306
1398
1307
1399
/** ParArgumentExprss ::= {ParArgumentExprs}
1400
+ *
1401
+ * Special treatment for arguments of primary class constructor
1402
+ * annotations. If an argument list returns `List(ParamNotArg)`
1403
+ * ignore it, and return prefix parsed before that list instead.
1308
1404
*/
1309
1405
def parArgumentExprss (fn : Tree ): Tree =
1310
- if (in.token == LPAREN ) parArgumentExprss(Apply (fn, parArgumentExprs()))
1406
+ if (in.token == LPAREN ) {
1407
+ val first = parArgumentExprs()
1408
+ if (inClassConstrAnnots && first == ParamNotArg :: Nil ) fn
1409
+ else parArgumentExprss(Apply (fn, first))
1410
+ }
1311
1411
else fn
1312
1412
1313
1413
/** BlockExpr ::= `{' (CaseClauses | Block) `}'
@@ -2094,21 +2194,15 @@ object Parsers {
2094
2194
*/
2095
2195
def classConstr (owner : Name , isCaseClass : Boolean = false ): DefDef = atPos(in.lastOffset) {
2096
2196
val tparams = typeParamClauseOpt(ParamOwner .Class )
2097
- val cmods = constrModsOpt(owner)
2197
+ val cmods = fromWithinClassConstr( constrModsOpt(owner) )
2098
2198
val vparamss = paramClauses(owner, isCaseClass)
2099
2199
makeConstructor(tparams, vparamss).withMods(cmods)
2100
2200
}
2101
2201
2102
- /** ConstrMods ::= AccessModifier
2103
- * | Annotation {Annotation} (AccessModifier | `this')
2202
+ /** ConstrMods ::= {Annotation} [AccessModifier]
2104
2203
*/
2105
- def constrModsOpt (owner : Name ): Modifiers = {
2106
- val mods = modifiers(accessModifierTokens, annotsAsMods())
2107
- if (mods.hasAnnotations && ! mods.hasFlags)
2108
- if (in.token == THIS ) in.nextToken()
2109
- else syntaxError(AnnotatedPrimaryConstructorRequiresModifierOrThis (owner), mods.annotations.last.pos)
2110
- mods
2111
- }
2204
+ def constrModsOpt (owner : Name ): Modifiers =
2205
+ modifiers(accessModifierTokens, annotsAsMods())
2112
2206
2113
2207
/** ObjectDef ::= id TemplateOpt
2114
2208
*/
0 commit comments