Skip to content

Fix parsing Java annotation values #11809

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 19, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 17 additions & 30 deletions compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -359,19 +359,6 @@ object JavaParsers {
* but instead we skip entire annotation silently.
*/
def annotation(): Option[Tree] = {
object LiteralT:
def unapply(token: Token) = Option(token match {
case TRUE => true
case FALSE => false
case CHARLIT => in.name(0)
case INTLIT => in.intVal(false).toInt
case LONGLIT => in.intVal(false)
case FLOATLIT => in.floatVal(false).toFloat
case DOUBLELIT => in.floatVal(false)
case STRINGLIT => in.name.toString
case _ => null
}).map(Constant(_))

def classOrId(): Tree =
val id = qualId()
if in.lookaheadToken == CLASS then
Expand All @@ -398,17 +385,17 @@ object JavaParsers {
}

def argValue(): Option[Tree] =
val tree = in.token match {
case LiteralT(c) =>
val tree = atSpan(in.offset)(Literal(c))
in.nextToken()
Some(tree)
case AT =>
in.nextToken()
annotation()
case IDENTIFIER => Some(classOrId())
case LBRACE => array()
case _ => None
val tree = tryConstant match {
case Some(c) =>
Some(atSpan(in.offset)(Literal(c)))
case _ => in.token match {
case AT =>
in.nextToken()
annotation()
case IDENTIFIER => Some(classOrId())
case LBRACE => array()
case _ => None
}
}
if in.token == COMMA || in.token == RBRACE || in.token == RPAREN then
tree
Expand Down Expand Up @@ -716,11 +703,7 @@ object JavaParsers {

in.nextToken() // EQUALS
if (mods.is(Flags.JavaStatic) && mods.is(Flags.Final)) {
val neg = in.token match {
case MINUS | BANG => in.nextToken(); true
case _ => false
}
tryLiteral(neg).map(forConst).getOrElse(tpt1)
tryConstant.map(forConst).getOrElse(tpt1)
}
else tpt1
}
Expand Down Expand Up @@ -976,7 +959,11 @@ object JavaParsers {
case _ => in.nextToken(); syntaxError("illegal start of type declaration", skipIt = true); List(errorTypeTree)
}

def tryLiteral(negate: Boolean = false): Option[Constant] = {
def tryConstant: Option[Constant] = {
val negate = in.token match {
case MINUS | BANG => in.nextToken(); true
case _ => false
}
val l = in.token match {
case TRUE => !negate
case FALSE => negate
Expand Down
4 changes: 4 additions & 0 deletions tests/run/annot-arg-value-in-java.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
J:
List(Apply(Select(New(Ident(SuppressWarnings)),<init>),List(NamedArg(value,JavaSeqLiteral(List(Literal(Constant(a))), TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String)])))))
List(Apply(Select(New(Ident(SuppressWarnings)),<init>),List(NamedArg(value,JavaSeqLiteral(List(Literal(Constant(b))), TypeTree[TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String)])))))
List(Apply(Select(New(Ident(SuppressWarnings)),<init>),List(NamedArg(value,Apply(Apply(TypeApply(Select(Select(Select(Ident(_root_),scala),Array),apply),List(TypeTree[TypeVar(TypeParamRef(T) -> TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String))])),List(Typed(SeqLiteral(List(Literal(Constant(c)), Literal(Constant(d))),TypeTree[TypeVar(TypeParamRef(T) -> TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String))]),TypeTree[AppliedType(TypeRef(ThisType(TypeRef(NoPrefix,module class scala)),class <repeated>),List(TypeVar(TypeParamRef(T) -> TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String))))]))),List(Apply(TypeApply(Select(Ident(ClassTag),apply),List(TypeTree[TypeVar(TypeParamRef(T) -> TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String))])),List(Literal(Constant(TypeRef(ThisType(TypeRef(NoPrefix,module class lang)),class String)))))))))))
7 changes: 7 additions & 0 deletions tests/run/annot-arg-value-in-java/AnnoMacro.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import scala.quoted.*

inline def annots(inline c: String): List[String] = ${ annotsImpl('c) }

def annotsImpl(c: Expr[String])(using Quotes): Expr[List[String]] =
import quotes.reflect.*
Expr(Symbol.requiredClass(c.valueOrError).declaredMethods.map(_.annotations.toString))
8 changes: 8 additions & 0 deletions tests/run/annot-arg-value-in-java/J.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
public class J {
@SuppressWarnings(value = "a")
public void f1() {}
@SuppressWarnings("b")
public void f2() {}
@SuppressWarnings({"c", "d"})
public void f3() {}
}
4 changes: 4 additions & 0 deletions tests/run/annot-arg-value-in-java/S.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@main def Test =
inline val c = "J"
println(c + ":")
annots(c).foreach(println)