@@ -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))
@@ -1279,20 +1314,79 @@ object Parsers {
1279
1314
if (in.token == RPAREN ) Nil else commaSeparated(exprInParens)
1280
1315
1281
1316
/** ParArgumentExprs ::= `(' [ExprsInParens] `)'
1282
- * | `(' [ExprsInParens `,'] PostfixExpr `:' `_' `*' ')' \
1283
- */
1284
- def parArgumentExprs (): List [Tree ] =
1285
- inParens(if (in.token == RPAREN ) Nil else commaSeparated(argumentExpr))
1317
+ * | `(' [ExprsInParens `,'] PostfixExpr `:' `_' `*' ')'
1318
+ *
1319
+ * Special treatment for arguments of primary class constructor
1320
+ * annotations. All empty argument lists `(` `)` following the first
1321
+ * get represented as `List(ParamNotArg)` instead of `Nil`, indicating that
1322
+ * the token sequence should be interpreted as an empty parameter clause
1323
+ * instead. `ParamNotArg` can also be produced when parsing the first
1324
+ * argument (see `classConstrAnnotExpr`).
1325
+ *
1326
+ * The method affects `lookaheadTokens` as a side effect.
1327
+ * If the argument list parses as `List(ParamNotArg)`, `lookaheadTokens`
1328
+ * contains the tokens that need to be replayed to parse the parameter clause.
1329
+ * Otherwise, `lookaheadTokens` is empty.
1330
+ */
1331
+ def parArgumentExprs (first : Boolean = false ): List [Tree ] = {
1332
+ if (inClassConstrAnnots) {
1333
+ assert(lookaheadTokens.isEmpty)
1334
+ saveLookahead()
1335
+ accept(LPAREN )
1336
+ val args =
1337
+ if (in.token == RPAREN )
1338
+ if (first) Nil // first () counts as annotation argument
1339
+ else ParamNotArg :: Nil
1340
+ else {
1341
+ openParens.change(LPAREN , + 1 )
1342
+ try commaSeparated(argumentExpr)
1343
+ finally openParens.change(LPAREN , - 1 )
1344
+ }
1345
+ if (args == ParamNotArg :: Nil )
1346
+ in.adjustSepRegions(RPAREN ) // simulate `)` without requiring it
1347
+ else {
1348
+ lookaheadTokens.clear()
1349
+ accept(RPAREN )
1350
+ }
1351
+ args
1352
+ }
1353
+ else
1354
+ inParens(if (in.token == RPAREN ) Nil else commaSeparated(argumentExpr))
1355
+ }
1286
1356
1287
1357
/** ArgumentExprs ::= ParArgumentExprs
1288
1358
* | [nl] BlockExpr
1289
1359
*/
1290
1360
def argumentExprs (): List [Tree ] =
1291
1361
if (in.token == LBRACE ) blockExpr() :: Nil else parArgumentExprs()
1292
1362
1293
- val argumentExpr = () => exprInParens() match {
1294
- case a @ Assign (Ident (id), rhs) => cpy.NamedArg (a)(id, rhs)
1295
- case e => e
1363
+ val argumentExpr = () => {
1364
+ val arg =
1365
+ if (inClassConstrAnnots && lookaheadTokens.nonEmpty) classConstrAnnotExpr()
1366
+ else exprInParens()
1367
+ arg match {
1368
+ case arg @ Assign (Ident (id), rhs) => cpy.NamedArg (arg)(id, rhs)
1369
+ case arg => arg
1370
+ }
1371
+ }
1372
+
1373
+ /** Handle first argument of an argument list to an annotation of
1374
+ * a primary class constructor. If the current token either cannot
1375
+ * start an expression or is an identifier and is followed by `:`,
1376
+ * stop parsing the rest of the expression and return `EmptyTree`,
1377
+ * indicating that we should re-parse the expression as a parameter clause.
1378
+ * Otherwise parse as normal.
1379
+ */
1380
+ def classConstrAnnotExpr () = {
1381
+ if (in.token == IDENTIFIER ) {
1382
+ saveLookahead()
1383
+ postfixExpr() match {
1384
+ case Ident (_) if in.token == COLON => ParamNotArg
1385
+ case t => expr1Rest(t, Location .InParens )
1386
+ }
1387
+ }
1388
+ else if (isExprIntro) exprInParens()
1389
+ else ParamNotArg
1296
1390
}
1297
1391
1298
1392
/** ArgumentExprss ::= {ArgumentExprs}
@@ -1304,9 +1398,17 @@ object Parsers {
1304
1398
}
1305
1399
1306
1400
/** ParArgumentExprss ::= {ParArgumentExprs}
1401
+ *
1402
+ * Special treatment for arguments of primary class constructor
1403
+ * annotations. If an argument list returns `List(ParamNotArg)`
1404
+ * ignore it, and return prefix parsed before that list instead.
1307
1405
*/
1308
1406
def parArgumentExprss (fn : Tree ): Tree =
1309
- if (in.token == LPAREN ) parArgumentExprss(Apply (fn, parArgumentExprs()))
1407
+ if (in.token == LPAREN ) {
1408
+ val args = parArgumentExprs(first = ! fn.isInstanceOf [Trees .Apply [_]])
1409
+ if (inClassConstrAnnots && args == ParamNotArg :: Nil ) fn
1410
+ else parArgumentExprss(Apply (fn, args))
1411
+ }
1310
1412
else fn
1311
1413
1312
1414
/** BlockExpr ::= `{' (CaseClauses | Block) `}'
@@ -2093,21 +2195,15 @@ object Parsers {
2093
2195
*/
2094
2196
def classConstr (owner : Name , isCaseClass : Boolean = false ): DefDef = atPos(in.lastOffset) {
2095
2197
val tparams = typeParamClauseOpt(ParamOwner .Class )
2096
- val cmods = constrModsOpt(owner)
2198
+ val cmods = fromWithinClassConstr( constrModsOpt(owner) )
2097
2199
val vparamss = paramClauses(owner, isCaseClass)
2098
2200
makeConstructor(tparams, vparamss).withMods(cmods)
2099
2201
}
2100
2202
2101
- /** ConstrMods ::= AccessModifier
2102
- * | Annotation {Annotation} (AccessModifier | `this')
2203
+ /** ConstrMods ::= {Annotation} [AccessModifier]
2103
2204
*/
2104
- def constrModsOpt (owner : Name ): Modifiers = {
2105
- val mods = modifiers(accessModifierTokens, annotsAsMods())
2106
- if (mods.hasAnnotations && ! mods.hasFlags)
2107
- if (in.token == THIS ) in.nextToken()
2108
- else syntaxError(AnnotatedPrimaryConstructorRequiresModifierOrThis (owner), mods.annotations.last.pos)
2109
- mods
2110
- }
2205
+ def constrModsOpt (owner : Name ): Modifiers =
2206
+ modifiers(accessModifierTokens, annotsAsMods())
2111
2207
2112
2208
/** ObjectDef ::= id TemplateOpt
2113
2209
*/
0 commit comments