@@ -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))
@@ -1280,20 +1315,77 @@ object Parsers {
12801315 if (in.token == RPAREN ) Nil else commaSeparated(exprInParens)
12811316
12821317 /** 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+ }
12871355
12881356 /** ArgumentExprs ::= ParArgumentExprs
12891357 * | [nl] BlockExpr
12901358 */
12911359 def argumentExprs (): List [Tree ] =
12921360 if (in.token == LBRACE ) blockExpr() :: Nil else parArgumentExprs()
12931361
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
12971389 }
12981390
12991391 /** ArgumentExprss ::= {ArgumentExprs}
@@ -1305,9 +1397,17 @@ object Parsers {
13051397 }
13061398
13071399 /** 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.
13081404 */
13091405 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+ }
13111411 else fn
13121412
13131413 /** BlockExpr ::= `{' (CaseClauses | Block) `}'
@@ -2094,21 +2194,15 @@ object Parsers {
20942194 */
20952195 def classConstr (owner : Name , isCaseClass : Boolean = false ): DefDef = atPos(in.lastOffset) {
20962196 val tparams = typeParamClauseOpt(ParamOwner .Class )
2097- val cmods = constrModsOpt(owner)
2197+ val cmods = fromWithinClassConstr( constrModsOpt(owner) )
20982198 val vparamss = paramClauses(owner, isCaseClass)
20992199 makeConstructor(tparams, vparamss).withMods(cmods)
21002200 }
21012201
2102- /** ConstrMods ::= AccessModifier
2103- * | Annotation {Annotation} (AccessModifier | `this')
2202+ /** ConstrMods ::= {Annotation} [AccessModifier]
21042203 */
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())
21122206
21132207 /** ObjectDef ::= id TemplateOpt
21142208 */
0 commit comments