Skip to content

Fix #2760: Support defaults in Java annotations parsed from sources #3865

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 3 commits into from
Mar 26, 2018
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ object StdNames {
val in: N = "in"
val info: N = "info"
val inlinedEquals: N = "inlinedEquals"
val internal: N = "internal"
val isArray: N = "isArray"
val isDefinedAt: N = "isDefinedAt"
val isDefinedAtImpl: N = "$isDefinedAt"
Expand Down
23 changes: 17 additions & 6 deletions compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ object JavaParsers {
// A dummy first constructor is needed for Java classes so that the real constructors see the
// import of the companion object. The constructor has parameter of type Unit so no Java code
// can call it.
// This also avoids clashes between the constructor parameter names and member names.
if (needsDummyConstr) {
stats1 = constr1 :: stats1
constr1 = makeConstructor(List(scalaDot(tpnme.Unit)), tparams, Flags.JavaDefined | Flags.PrivateLocal)
Expand All @@ -132,8 +133,8 @@ object JavaParsers {

def makeSyntheticParam(count: Int, tpt: Tree): ValDef =
makeParam(nme.syntheticParamName(count), tpt)
def makeParam(name: TermName, tpt: Tree): ValDef =
ValDef(name, tpt, EmptyTree).withMods(Modifiers(Flags.JavaDefined | Flags.ParamAccessor))
def makeParam(name: TermName, tpt: Tree, defaultValue: Tree = EmptyTree): ValDef =
ValDef(name, tpt, defaultValue).withMods(Modifiers(Flags.JavaDefined | Flags.Param))

def makeConstructor(formals: List[Tree], tparams: List[TypeDef], flags: FlagSet = Flags.JavaDefined) = {
val vparams = formals.zipWithIndex.map { case (p, i) => makeSyntheticParam(i + 1, p) }
Expand Down Expand Up @@ -515,7 +516,7 @@ object JavaParsers {
if (parentToken == AT && in.token == DEFAULT) {
val annot =
atPos(nameOffset) {
New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), Nil)
New(Select(Select(scalaDot(nme.annotation), nme.internal), tpnme.AnnotationDefaultATTR), Nil)
}
mods1 = mods1 withAddedAnnotation annot
val unimplemented = unimplementedExpr
Expand Down Expand Up @@ -772,12 +773,22 @@ object JavaParsers {
val name = identForType()
val (statics, body) = typeBody(AT, name, List())
val constructorParams = body.collect {
case dd: DefDef => makeParam(dd.name, dd.tpt)
case dd: DefDef =>
val hasDefault =
dd.mods.annotations.exists {
case Apply(Select(New(Select(_, tpnme.AnnotationDefaultATTR)), nme.CONSTRUCTOR), Nil) =>
true
case _ =>
false
}
// If the annotation has a default value we don't need to parse it, providing
// any value at all is enough to typecheck usages of annotations correctly.
val defaultParam = if (hasDefault) unimplementedExpr else EmptyTree
makeParam(dd.name, dd.tpt, defaultParam)
}
val constr = DefDef(nme.CONSTRUCTOR,
List(), List(constructorParams), TypeTree(), EmptyTree).withMods(Modifiers(Flags.JavaDefined))
val body1 = body.filterNot(_.isInstanceOf[DefDef])
val templ = makeTemplate(annotationParents, constr :: body1, List(), false)
val templ = makeTemplate(annotationParents, constr :: body, List(), true)
val annot = atPos(start, nameOffset) {
TypeDef(name, templ).withMods(mods | Flags.Abstract)
}
Expand Down
7 changes: 7 additions & 0 deletions tests/run/i2760/Fork.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
public @interface Fork {
int value() default -1;
int warmups() default -1;
}
9 changes: 9 additions & 0 deletions tests/run/i2760/Test.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@Fork(value = 16) class HasFork

object Test {
def main(args: Array[String]): Unit = {
val fork = classOf[HasFork].getAnnotation(classOf[Fork])
assert(fork.value == 16, s"fork.value is ${fork.value} but should have been 16")
assert(fork.warmups == -1, s"fork.warmups is ${fork.warmups} but should have been -1")
}
}