Skip to content

Commit d3f46fc

Browse files
committed
Skip java annotations with complex args in parser
1 parent 74ab3d1 commit d3f46fc

File tree

3 files changed

+66
-35
lines changed

3 files changed

+66
-35
lines changed

compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,20 @@ 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] = {
349362
object LiteralT:
@@ -372,60 +385,64 @@ object JavaParsers {
372385
)
373386
else id
374387

375-
def array(): Tree =
388+
def array(): Option[Tree] =
376389
accept(LBRACE)
377-
val buffer = ListBuffer[Tree]()
378-
while (in.token != RBRACE) {
390+
val buffer = ListBuffer[Option[Tree]]()
391+
while in.token != RBRACE do
379392
buffer += argValue()
380-
if (in.token == COMMA) in.nextToken() // using this instead of repsep allows us to handle trailing commas
381-
}
382-
val ok = !buffer.contains(EmptyTree)
383-
in.token match {
384-
case RBRACE if ok =>
385-
accept(RBRACE)
386-
Apply(scalaDot(nme.Array), buffer.toList)
387-
case _ =>
388-
skipTo(RBRACE)
389-
EmptyTree
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)
390398
}
391399

392-
def argValue(): Tree =
393-
in.token match {
400+
def argValue(): Option[Tree] =
401+
val tree = in.token match {
394402
case LiteralT(c) =>
395403
val tree = atSpan(in.offset)(Literal(c))
396404
in.nextToken()
397-
tree
405+
Some(tree)
398406
case AT =>
399407
in.nextToken()
400-
annotation().get
401-
case IDENTIFIER => classOrId()
408+
annotation()
409+
case IDENTIFIER => Some(classOrId())
402410
case LBRACE => array()
403-
case _ => EmptyTree
411+
case _ => None
404412
}
413+
if in.token == COMMA || in.token == RBRACE || in.token == RPAREN then
414+
tree
415+
else
416+
skipTo(COMMA, RBRACE, RPAREN)
417+
None
405418

406-
def annArg(): Tree =
407-
if (in.token == IDENTIFIER && in.lookaheadToken == EQUALS)
408-
val name = ident()
419+
def annArg(): Option[Tree] =
420+
val name = if (in.token == IDENTIFIER && in.lookaheadToken == EQUALS)
421+
val n = ident()
409422
accept(EQUALS)
410-
val argv = argValue()
411-
NamedArg(name, argv)
412-
423+
n
413424
else
414-
NamedArg(nme.value, argValue())
425+
nme.value
426+
argValue().map(NamedArg(name, _))
415427

416428

417429
val id = convertToTypeId(qualId())
418-
val args = if in.token == LPAREN then
430+
val args = ListBuffer[Option[Tree]]()
431+
if in.token == LPAREN then
419432
in.nextToken()
420-
val args = repsep(annArg, COMMA)
433+
if in.token != RPAREN then
434+
args += annArg()
435+
while in.token == COMMA do
436+
in.nextToken()
437+
args += annArg()
421438
accept(RPAREN)
422-
args
423-
else Nil
424439

425-
Some(Apply(
426-
Select(New(id), nme.CONSTRUCTOR),
427-
args
428-
))
440+
Option.unless(args contains None) {
441+
Apply(
442+
Select(New(id), nme.CONSTRUCTOR),
443+
args.flatten.toList
444+
)
445+
}
429446
}
430447

431448
def modifiers(inInterface: Boolean): Modifiers = {

tests/run/java-annot-params/Annots_0.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,18 @@
6565
Class<?> clazz();
6666
String[] value();
6767
}
68+
69+
@Retention(RetentionPolicy.RUNTIME)
70+
@interface ShouldNotCrash {
71+
String value();
72+
}
73+
74+
@Retention(RetentionPolicy.RUNTIME)
75+
@interface ShouldAlsoNotCrash {
76+
String value();
77+
int[] ints();
78+
}
79+
6880
class A {
6981
static final String CONST = "VALUE OF CONST";
7082
}

tests/run/java-annot-params/Use_0.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@
1717
value = "ABC",
1818
clazz = A.class
1919
)
20+
@ShouldNotCrash(false ? "A" + A.CONST : "B")
21+
@ShouldAlsoNotCrash(value = "C", ints = { 1, 2, 3, 5 - 1 })
2022
public class Use_0 {}

0 commit comments

Comments
 (0)