@@ -246,7 +246,7 @@ object JavaParsers {
246246
247247 def qualId (): RefTree = {
248248 var t : RefTree = atSpan(in.offset) { Ident (ident()) }
249- while (in.token == DOT ) {
249+ while (in.token == DOT && in.lookaheadToken == IDENTIFIER ) {
250250 in.nextToken()
251251 t = atSpan(t.span.start, in.offset) { Select (t, ident()) }
252252 }
@@ -343,22 +343,105 @@ object JavaParsers {
343343 annots.toList
344344 }
345345
346- /** Annotation ::= TypeName [`(` AnnotationArgument {`,` AnnotationArgument} `)`]
346+ /** Annotation ::= TypeName [`(` [AnnotationArgument {`,` AnnotationArgument}] `)`]
347+ * AnnotationArgument ::= ElementValuePair | ELementValue
348+ * ElementValuePair ::= Identifier `=` ElementValue
349+ * ElementValue ::= ConstExpressionSubset
350+ * | ElementValueArrayInitializer
351+ * | Annotation
352+ * ElementValueArrayInitializer ::= `{` [ElementValue {`,` ElementValue}] [`,`] `}`
353+ * ConstExpressionSubset ::= Literal
354+ * | QualifiedName
355+ * | ClassLiteral
356+ *
357+ * We support only subset of const expressions expected in this context by java.
358+ * If we encounter expression that we cannot parse, we do not raise parsing error,
359+ * but instead we skip entire annotation silently.
347360 */
348361 def annotation (): Option [Tree ] = {
349- val id = convertToTypeId(qualId())
350- // only parse annotations without arguments
351- if (in.token == LPAREN && in.lookaheadToken != RPAREN ) {
352- skipAhead()
353- accept(RPAREN )
354- None
355- }
356- else {
357- if (in.token == LPAREN ) {
362+ object LiteralT :
363+ def unapply (token : Token ) = Option (token match {
364+ case TRUE => true
365+ case FALSE => false
366+ case CHARLIT => in.name(0 )
367+ case INTLIT => in.intVal(false ).toInt
368+ case LONGLIT => in.intVal(false )
369+ case FLOATLIT => in.floatVal(false ).toFloat
370+ case DOUBLELIT => in.floatVal(false )
371+ case STRINGLIT => in.name.toString
372+ case _ => null
373+ }).map(Constant (_))
374+
375+ def classOrId (): Tree =
376+ val id = qualId()
377+ if in.lookaheadToken == CLASS then
358378 in.nextToken()
359- accept(RPAREN )
379+ accept(CLASS )
380+ TypeApply (
381+ Select (
382+ scalaDot(nme.Predef ),
383+ nme.classOf ),
384+ convertToTypeId(id) :: Nil
385+ )
386+ else id
387+
388+ def array (): Option [Tree ] =
389+ accept(LBRACE )
390+ val buffer = ListBuffer [Option [Tree ]]()
391+ while in.token != RBRACE do
392+ buffer += argValue()
393+ if in.token == COMMA then
394+ in.nextToken() // using this instead of repsep allows us to handle trailing commas
395+ accept(RBRACE )
396+ Option .unless(buffer contains None ) {
397+ Apply (scalaDot(nme.Array ), buffer.flatten.toList)
398+ }
399+
400+ def argValue (): Option [Tree ] =
401+ val tree = in.token match {
402+ case LiteralT (c) =>
403+ val tree = atSpan(in.offset)(Literal (c))
404+ in.nextToken()
405+ Some (tree)
406+ case AT =>
407+ in.nextToken()
408+ annotation()
409+ case IDENTIFIER => Some (classOrId())
410+ case LBRACE => array()
411+ case _ => None
360412 }
361- Some (ensureApplied(Select (New (id), nme.CONSTRUCTOR )))
413+ if in.token == COMMA || in.token == RBRACE || in.token == RPAREN then
414+ tree
415+ else
416+ skipTo(COMMA , RBRACE , RPAREN )
417+ None
418+
419+ def annArg (): Option [Tree ] =
420+ val name = if (in.token == IDENTIFIER && in.lookaheadToken == EQUALS )
421+ val n = ident()
422+ accept(EQUALS )
423+ n
424+ else
425+ nme.value
426+ argValue().map(NamedArg (name, _))
427+
428+
429+ val id = convertToTypeId(qualId())
430+ val args = ListBuffer [Option [Tree ]]()
431+ if in.token == LPAREN then
432+ in.nextToken()
433+ if in.token != RPAREN then
434+ args += annArg()
435+ while in.token == COMMA do
436+ in.nextToken()
437+ args += annArg()
438+ accept(RPAREN )
439+
440+ Option .unless(args contains None ) {
441+ Apply (
442+ Select (New (id), nme.CONSTRUCTOR ),
443+ args.flatten.toList
444+ )
362445 }
363446 }
364447
0 commit comments