@@ -294,6 +294,41 @@ object Parsers {
294294 } finally inFunReturnType = saved
295295 }
296296
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+
297332 def migrationWarningOrError (msg : String , offset : Int = in.offset) =
298333 if (in.isScala2Mode)
299334 ctx.migrationWarning(msg, source atPos Position (offset))
@@ -1279,20 +1314,79 @@ object Parsers {
12791314 if (in.token == RPAREN ) Nil else commaSeparated(exprInParens)
12801315
12811316 /** 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+ }
12861356
12871357 /** ArgumentExprs ::= ParArgumentExprs
12881358 * | [nl] BlockExpr
12891359 */
12901360 def argumentExprs (): List [Tree ] =
12911361 if (in.token == LBRACE ) blockExpr() :: Nil else parArgumentExprs()
12921362
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
12961390 }
12971391
12981392 /** ArgumentExprss ::= {ArgumentExprs}
@@ -1304,9 +1398,17 @@ object Parsers {
13041398 }
13051399
13061400 /** 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.
13071405 */
13081406 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+ }
13101412 else fn
13111413
13121414 /** BlockExpr ::= `{' (CaseClauses | Block) `}'
@@ -2093,21 +2195,15 @@ object Parsers {
20932195 */
20942196 def classConstr (owner : Name , isCaseClass : Boolean = false ): DefDef = atPos(in.lastOffset) {
20952197 val tparams = typeParamClauseOpt(ParamOwner .Class )
2096- val cmods = constrModsOpt(owner)
2198+ val cmods = fromWithinClassConstr( constrModsOpt(owner) )
20972199 val vparamss = paramClauses(owner, isCaseClass)
20982200 makeConstructor(tparams, vparamss).withMods(cmods)
20992201 }
21002202
2101- /** ConstrMods ::= AccessModifier
2102- * | Annotation {Annotation} (AccessModifier | `this')
2203+ /** ConstrMods ::= {Annotation} [AccessModifier]
21032204 */
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())
21112207
21122208 /** ObjectDef ::= id TemplateOpt
21132209 */
0 commit comments