diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 6ffdb1654d96..c9e158b06ca2 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -95,6 +95,23 @@ trait ParsersCommon extends ScannersCommon { self => */ @inline final def makeParens(body: => List[Tree]): Parens = Parens(inParens(if (in.token == RPAREN) Nil else body)) + + /** {{{ part { `sep` part } }}}, or if sepFirst is true, {{{ { `sep` part } }}}. */ + final def tokenSeparated[T](separator: Token, sepFirst: Boolean, part: => T): List[T] = { + val ts = new ListBuffer[T] + if (!sepFirst) + ts += part + + while (in.token == separator) { + in.nextToken() + ts += part + } + ts.toList + } + + /** {{{ tokenSeparated }}}, with the separator fixed to commas. */ + @inline final def commaSeparated[T](part: => T): List[T] = + tokenSeparated(COMMA, sepFirst = false, part) } } @@ -791,20 +808,6 @@ self => errorTypeTree } } - - /** {{{ part { `sep` part } }}},or if sepFirst is true, {{{ { `sep` part } }}}. */ - final def tokenSeparated[T](separator: Token, sepFirst: Boolean, part: => T): List[T] = { - val ts = new ListBuffer[T] - if (!sepFirst) - ts += part - - while (in.token == separator) { - in.nextToken() - ts += part - } - ts.toList - } - @inline final def commaSeparated[T](part: => T): List[T] = tokenSeparated(COMMA, sepFirst = false, part) @inline final def caseSeparated[T](part: => T): List[T] = tokenSeparated(CASE, sepFirst = true, part) def readAnnots(part: => Tree): List[Tree] = tokenSeparated(AT, sepFirst = true, part) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index a95cb85f5824..7c88b61b5e11 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -40,6 +40,9 @@ trait ScannersCommon { } trait ScannerCommon extends CommonTokenData { + /** Consume and discard the next token. */ + def nextToken(): Unit + // things to fill in, in addition to buf, decodeUni which come from CharArrayReader def error(off: Offset, msg: String): Unit def incompleteInputError(off: Offset, msg: String): Unit diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 9f3d66dda17d..3a991510cbb5 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -242,11 +242,21 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { // -------------------- specific parsing routines ------------------ - def qualId(): RefTree = { - var t: RefTree = atPos(in.currentPos) { Ident(ident()) } - while (in.token == DOT) { + def qualId(orClassLiteral: Boolean = false): Tree = { + var t: Tree = atPos(in.currentPos) { Ident(ident()) } + var done = false + while (!done && in.token == DOT) { in.nextToken() - t = atPos(in.currentPos) { Select(t, ident()) } + t = atPos(in.currentPos) { + if (orClassLiteral && in.token == CLASS) { + in.nextToken() + done = true + val tpeArg = convertToTypeId(t) + TypeApply(Select(gen.mkAttributedRef(definitions.PredefModule), nme.classOf), tpeArg :: Nil) + } else { + Select(t, ident()) + } + } } t } @@ -275,7 +285,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { } def typ(): Tree = { - annotations() + annotations() // TODO: fix scala/bug#9883 (JSR 308) optArrayBrackets { if (in.token == FINAL) in.nextToken() if (in.token == IDENTIFIER) { @@ -334,20 +344,73 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { } def annotations(): List[Tree] = { - //var annots = new ListBuffer[Tree] + val annots = new ListBuffer[Tree] while (in.token == AT) { in.nextToken() - annotation() + val annot = annotation() + if (annot.nonEmpty) annots += annot } - List() // don't pass on annotations for now + annots.toList } - /** Annotation ::= TypeName [`(` AnnotationArgument {`,` AnnotationArgument} `)`] + /** Annotation ::= TypeName [`(` [AnnotationArgument {`,` AnnotationArgument}] `)`] */ - def annotation() { - qualId() - if (in.token == LPAREN) { skipAhead(); accept(RPAREN) } - else if (in.token == LBRACE) { skipAhead(); accept(RBRACE) } + def annotation(): Tree = { + def annArg(): Tree = { + def annVal(): Tree = { + tryLiteral() match { + case Some(lit) => atPos(in.currentPos)(Literal(lit)) + case _ if in.token == AT => + in.nextToken() + annotation() + case _ if in.token == LBRACE => + atPos(in.pos) { + val elts = inBracesOrNil(commaSeparated(annVal())) + if (elts.exists(_.isEmpty)) EmptyTree + else Apply(ArrayModule_overloadedApply, elts: _*) + } + case _ if in.token == IDENTIFIER => + qualId(orClassLiteral = true) + } + } + + if (in.token == IDENTIFIER) { + qualId(orClassLiteral = true) match { + case name: Ident if in.token == EQUALS => + in.nextToken() + /* name = value */ + val value = annVal() + if (value.isEmpty) EmptyTree else gen.mkNamedArg(name, value) + case rhs => + /* implicit `value` arg with constant value */ + gen.mkNamedArg(nme.value, rhs) + } + } else { + /* implicit `value` arg */ + val value = annVal() + if (value.isEmpty) EmptyTree else gen.mkNamedArg(nme.value, value) + } + } + + atPos(in.pos) { + val id = convertToTypeId(qualId()) + if (in.token == LPAREN) { + val saved = new JavaTokenData {}.copyFrom(in) // prep to bail if non-literals/identifiers + accept(LPAREN) + val args = + if (in.token == RPAREN) Nil + else commaSeparated(atPos(in.pos)(annArg())) + if (in.token == RPAREN) { + accept(RPAREN) + New(id, args :: Nil) + } else { + in.copyFrom(saved) + skipAhead() + accept(RPAREN) + EmptyTree + } + } else New(id, ListOfNil) + } } def modifiers(inInterface: Boolean): Modifiers = { @@ -361,7 +424,8 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { in.token match { case AT if (in.lookaheadToken != INTERFACE) => in.nextToken() - annotation() + val annot = annotation() + if (annot.nonEmpty) annots :+= annot case PUBLIC => isPackageAccess = false in.nextToken() @@ -419,10 +483,10 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { def typeParam(): TypeDef = atPos(in.currentPos) { - annotations() + val anns = annotations() val name = identForType() val hi = if (in.token == EXTENDS) { in.nextToken() ; bound() } else EmptyTree - TypeDef(Modifiers(Flags.JAVA | Flags.DEFERRED | Flags.PARAM), name, Nil, TypeBoundsTree(EmptyTree, hi)) + TypeDef(Modifiers(Flags.JAVA | Flags.DEFERRED | Flags.PARAM, tpnme.EMPTY, anns), name, Nil, TypeBoundsTree(EmptyTree, hi)) } def bound(): Tree = @@ -446,7 +510,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { def formalParam(): ValDef = { if (in.token == FINAL) in.nextToken() - annotations() + val anns = annotations() var t = typ() if (in.token == DOTDOTDOT) { in.nextToken() @@ -454,7 +518,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { AppliedTypeTree(scalaDot(tpnme.JAVA_REPEATED_PARAM_CLASS_NAME), List(t)) } } - varDecl(in.currentPos, Modifiers(Flags.JAVA | Flags.PARAM), t, ident().toTermName) + varDecl(in.currentPos, Modifiers(Flags.JAVA | Flags.PARAM, typeNames.EMPTY, anns), t, ident().toTermName) } def optThrows() { @@ -841,7 +905,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { } def enumConst(enumType: Tree): (ValDef, Boolean) = { - annotations() + val anns = annotations() var hasClassBody = false val res = atPos(in.currentPos) { val name = ident() @@ -856,7 +920,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { skipAhead() accept(RBRACE) } - ValDef(Modifiers(Flags.JAVA_ENUM | Flags.STABLE | Flags.JAVA | Flags.STATIC), name.toTermName, enumType, blankExpr) + ValDef(Modifiers(Flags.JAVA_ENUM | Flags.STABLE | Flags.JAVA | Flags.STATIC, typeNames.EMPTY, anns), name.toTermName, enumType, blankExpr) } (res, hasClassBody) } @@ -894,10 +958,10 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { var pos = in.currentPos val pkg: RefTree = if (in.token == AT || in.token == PACKAGE) { - annotations() + annotations() // TODO: put these somewhere? pos = in.currentPos accept(PACKAGE) - val pkg = qualId() + val pkg = qualId().asInstanceOf[RefTree] accept(SEMI) pkg } else { diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala index a25c51eaf358..391fb030831e 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala @@ -52,12 +52,13 @@ trait JavaScanners extends ast.parser.ScannersCommon { /** the base of a number */ var base: Int = 0 - def copyFrom(td: JavaTokenData) = { + def copyFrom(td: JavaTokenData): this.type = { this.token = td.token this.pos = td.pos this.lastPos = td.lastPos this.name = td.name this.base = td.base + this } } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 3eeb2fc1f9bf..1a048537c510 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -851,8 +851,15 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { case tpnme.RuntimeAnnotationATTR => val numAnnots = u2 + val annots = new ListBuffer[AnnotationInfo] for (n <- 0 until numAnnots; annot <- parseAnnotation(u2)) - sym.addAnnotation(annot) + annots += annot + /* `sym.withAnnotations(annots)`, like `sym.addAnnotation(annot)`, prepends, + * so if we parsed in classfile order we would wind up with the annotations + * in reverse order in `sym.annotations`. Instead we just read them out the + * other way around, for now. TODO: sym.addAnnotation add to the end? + */ + sym.setAnnotations(sym.annotations ::: annots.toList) // TODO 1: parse runtime visible annotations on parameters // case tpnme.RuntimeParamAnnotationATTR diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 27b55d5d4ac5..0bc3aa346591 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3887,6 +3887,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case Typed(t, _) => tree2ConstArg(t, pt) + case tree if unit.isJava && pt.typeSymbol == ArrayClass => + /* If we get here, we have a Java array annotation argument which was passed + * as a single value, and needs to be wrapped. */ + trees2ConstArg(tree :: Nil, pt.typeArgs.head) + case tree => tryConst(tree, pt) } diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index 0594ff25ae5e..a2fad0c2dd56 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -1369,7 +1369,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") val symbols = Set(UnitClass, BooleanClass, ByteClass, ShortClass, IntClass, LongClass, FloatClass, - DoubleClass, NilModule, ListClass) ++ TupleClass.seq + DoubleClass, NilModule, ListClass, PredefModule) ++ TupleClass.seq ++ ArrayModule_overloadedApply.alternatives symbols.foreach(_.initialize) } diff --git a/src/partest-extras/scala/tools/partest/BytecodeTest.scala b/src/partest-extras/scala/tools/partest/BytecodeTest.scala index 309a6d49c482..35c487ba673b 100644 --- a/src/partest-extras/scala/tools/partest/BytecodeTest.scala +++ b/src/partest-extras/scala/tools/partest/BytecodeTest.scala @@ -124,6 +124,10 @@ abstract class BytecodeTest { } // loading + protected def getField(classNode: ClassNode, name: String): FieldNode = + classNode.fields.asScala.find(_.name == name) getOrElse + sys.error(s"Didn't find field '$name' in class '${classNode.name}'") + protected def getMethod(classNode: ClassNode, name: String): MethodNode = classNode.methods.asScala.find(_.name == name) getOrElse sys.error(s"Didn't find method '$name' in class '${classNode.name}'") diff --git a/test/files/presentation/parse-invariants/src/a/A.java b/test/files/presentation/parse-invariants/src/a/A.java index a04478149103..fa81c6699160 100644 --- a/test/files/presentation/parse-invariants/src/a/A.java +++ b/test/files/presentation/parse-invariants/src/a/A.java @@ -1,5 +1,10 @@ package syntax; +@NoArgs +@Empty() +@Simple(n = 1, c = '2', f = 6.7f, d = 8.9, s = "t", z = A.class, e = P.Pluto, a = @C.I("t")) +@Arrays({ @Array({0, 1, C._2}), @Array(3) }) +@Deprecated class A { transient volatile int x; strictfp void test() { @@ -11,6 +16,7 @@ synchronized void syncMethod() {} void thrower() throws Throwable {} + @Deprecated void deprecated() {} } strictfp class B {} \ No newline at end of file diff --git a/test/files/run/t4788-separate-compilation.check b/test/files/run/t4788-separate-compilation.check index 618fddfea324..38381c28a783 100644 --- a/test/files/run/t4788-separate-compilation.check +++ b/test/files/run/t4788-separate-compilation.check @@ -1,5 +1,9 @@ Some(@Ljava/lang/Deprecated;()) None -Some(@LSAnnotation;()) -Some(@LCAnnotation;()) +None +Some(@LCAnnotation;() // invisible) Some(@LRAnnotation;()) +Some(@LAnnWithArgs_0$Ann;(value="literal") // invisible) +Some(@LAnnWithArgs_0$Ann;(value="muk") // invisible) +Some(@LAnnWithArgs_0$Ann;(value="mukja") // invisible) +Some(@LAnnWithArgs_0$Ann;(value="mukja") // invisible) diff --git a/test/files/run/t4788-separate-compilation/AnnWithArgs_0.java b/test/files/run/t4788-separate-compilation/AnnWithArgs_0.java new file mode 100644 index 000000000000..898f61291d13 --- /dev/null +++ b/test/files/run/t4788-separate-compilation/AnnWithArgs_0.java @@ -0,0 +1,19 @@ +public class AnnWithArgs_0 { + public static @interface Ann { + String value(); + } + + @Ann("literal") + public static int x1 = 1; + + @Ann(AnnWithArgs_0.strS) + public static int x2 = 2; + + @Ann("muk" + "ja") + public static int x3 = 3; + + @Ann(AnnWithArgs_0.strS + "ja") + public static int x4 = 4; + + public static final String strS = "muk"; +} diff --git a/test/files/run/t4788-separate-compilation/Test_2.scala b/test/files/run/t4788-separate-compilation/Test_2.scala index b4267e948aca..b73dce374af6 100644 --- a/test/files/run/t4788-separate-compilation/Test_2.scala +++ b/test/files/run/t4788-separate-compilation/Test_2.scala @@ -18,6 +18,19 @@ object Test extends BytecodeTest { .map(_.trim) } + def annotationsForField(className: String, fieldName: String): Option[String] = { + val field = getField(loadClassNode(className, skipDebugInfo = false), fieldName) + val textifier = new Textifier + field.accept(new TraceClassVisitor(null, textifier, null)) + + val fieldString = stringFromWriter(w => textifier.print(w)) + fieldString + .split('\n') + .filterNot(_.contains("@Lscala/reflect/ScalaSignature")) + .find(_.contains("@L")) + .map(_.trim) + } + def show { // It seems like @java.lang.Deprecated shows up in both the // Deprecated attribute and RuntimeVisibleAnnotation attribute, @@ -31,5 +44,10 @@ object Test extends BytecodeTest { println(annotationsForClass("S")) println(annotationsForClass("C")) println(annotationsForClass("R")) + + println(annotationsForField("AnnWithArgs_0", "x1")) + println(annotationsForField("AnnWithArgs_0", "x2")) + println(annotationsForField("AnnWithArgs_0", "x3")) + println(annotationsForField("AnnWithArgs_0", "x4")) } } diff --git a/test/files/run/t4788.check b/test/files/run/t4788.check index 618fddfea324..e4d6fa09e575 100644 --- a/test/files/run/t4788.check +++ b/test/files/run/t4788.check @@ -1,5 +1,9 @@ Some(@Ljava/lang/Deprecated;()) None -Some(@LSAnnotation;()) -Some(@LCAnnotation;()) +None +Some(@LCAnnotation;() // invisible) Some(@LRAnnotation;()) +Some(@LAnnWithArgs$Ann;(value="literal") // invisible) +Some(@LAnnWithArgs$Ann;(value="muk") // invisible) +Some(@LAnnWithArgs$Ann;(value="mukja") // invisible) +Some(@LAnnWithArgs$Ann;(value="mukja") // invisible) diff --git a/test/files/run/t4788/AnnWithArgs.java b/test/files/run/t4788/AnnWithArgs.java new file mode 100644 index 000000000000..bc42b1f0e4e4 --- /dev/null +++ b/test/files/run/t4788/AnnWithArgs.java @@ -0,0 +1,19 @@ +public class AnnWithArgs { + public static @interface Ann { + String value(); + } + + @Ann("literal") + public static int x1 = 1; + + @Ann(AnnWithArgs.strS) + public static int x2 = 2; + + @Ann("muk" + "ja") + public static int x3 = 3; + + @Ann(AnnWithArgs.strS + "ja") + public static int x4 = 4; + + public static final String strS = "muk"; +} diff --git a/test/files/run/t4788/Test.scala b/test/files/run/t4788/Test.scala index b4267e948aca..79936dc681dc 100644 --- a/test/files/run/t4788/Test.scala +++ b/test/files/run/t4788/Test.scala @@ -18,6 +18,19 @@ object Test extends BytecodeTest { .map(_.trim) } + def annotationsForField(className: String, fieldName: String): Option[String] = { + val field = getField(loadClassNode(className, skipDebugInfo = false), fieldName) + val textifier = new Textifier + field.accept(new TraceClassVisitor(null, textifier, null)) + + val fieldString = stringFromWriter(w => textifier.print(w)) + fieldString + .split('\n') + .filterNot(_.contains("@Lscala/reflect/ScalaSignature")) + .find(_.contains("@L")) + .map(_.trim) + } + def show { // It seems like @java.lang.Deprecated shows up in both the // Deprecated attribute and RuntimeVisibleAnnotation attribute, @@ -31,5 +44,10 @@ object Test extends BytecodeTest { println(annotationsForClass("S")) println(annotationsForClass("C")) println(annotationsForClass("R")) + + println(annotationsForField("AnnWithArgs", "x1")) + println(annotationsForField("AnnWithArgs", "x2")) + println(annotationsForField("AnnWithArgs", "x3")) + println(annotationsForField("AnnWithArgs", "x4")) } } diff --git a/test/files/run/t8928.javaopts b/test/files/run/t8928.javaopts new file mode 100644 index 000000000000..a8e6bbca18ae --- /dev/null +++ b/test/files/run/t8928.javaopts @@ -0,0 +1 @@ +-Dneeds.forked.jvm diff --git a/test/files/run/t8928/Annotated_0.java b/test/files/run/t8928/Annotated_0.java new file mode 100644 index 000000000000..c3eef08d1713 --- /dev/null +++ b/test/files/run/t8928/Annotated_0.java @@ -0,0 +1,19 @@ +package test; + +// This should stay in sync with Annotated_1 to test annotations defined in the same compilation run + +@NoArgs_0 +@Simple_0(_byte = 1, _char = '2', _short = Simple_0.THREE, _int = 4, _long = 5, _float = 6.7f, _double = 8.9, _string = "ten", _class = Object.class) +@Nested_0( + inner = @Nested_0.Inner("turkey") +) +@Array_0.Repeated({ + @Array_0({8, 6, 7, 5, 3, 0, Annotated_0.NINE}), + @Array_0(6) +}) +@Enum_0(choice = Enum_0.Enum.ONE) +@Empty_0() +public class Annotated_0 { + public static final int NINE = 9; +} + diff --git a/test/files/run/t8928/Annotated_1.java b/test/files/run/t8928/Annotated_1.java new file mode 100644 index 000000000000..012fadc1087f --- /dev/null +++ b/test/files/run/t8928/Annotated_1.java @@ -0,0 +1,19 @@ +package test; + +// This should stay in sync with Annotated_0 to test annotations defined in an earlier compilation run + +@NoArgs_0 +@Simple_0(_byte = 1, _char = '2', _short = Simple_0.THREE, _int = 4, _long = 5, _float = 6.7f, _double = 8.9, _string = "ten", _class = Object.class) +@Nested_0( + inner = @Nested_0.Inner("turkey") +) +@Array_0.Repeated({ + @Array_0({8, 6, 7, 5, 3, 0, Annotated_1.NINE}), + @Array_0(6) +}) +@Enum_0(choice = Enum_0.Enum.ONE) +@Empty_0() +public class Annotated_1 { + public static final int NINE = 9; +} + diff --git a/test/files/run/t8928/Array_0.java b/test/files/run/t8928/Array_0.java new file mode 100644 index 000000000000..abc5503726b4 --- /dev/null +++ b/test/files/run/t8928/Array_0.java @@ -0,0 +1,14 @@ +package test; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Repeatable(Array_0.Repeated.class) +public @interface Array_0 { + int[] value(); + + @Retention(RetentionPolicy.RUNTIME) + public @interface Repeated { + Array_0[] value(); + } +} diff --git a/test/files/run/t8928/Checks_0.scala b/test/files/run/t8928/Checks_0.scala new file mode 100644 index 000000000000..bff5cd142ea3 --- /dev/null +++ b/test/files/run/t8928/Checks_0.scala @@ -0,0 +1,84 @@ +package test + +import reflect.api.Universe + +class Checks[U <: Universe with Singleton](val universe: U, ordered: Boolean) { + import universe._ + + def check(tpe: Type): Unit = { + val ObjectTpe = typeOf[Object] + + def assertMatch(name: String, v: Tree)(pf: PartialFunction[Tree, Any]): Unit = + if (!pf.isDefinedAt(v)) throw new AssertionError(s"$name: ${showRaw(v)}") + + val anns: List[Annotation] = tpe.typeSymbol.annotations + + anns match { + case List(noArgs, simple, nested, arrays, enum, empty) => + assert(noArgs.tree.tpe =:= typeOf[NoArgs_0]) + assert(noArgs.tree.children.size == 1) + + assert(simple.tree.tpe =:= typeOf[Simple_0]) + + val parameters: List[(String, Tree)] = + simple.tree.children.tail.map { + case AssignOrNamedArg(Ident(TermName(name)), value) => + name -> value + } + + /* Runtime reflection does not preserve ordering on java annotations, + * so check only when we're in a macro universe. */ + if (ordered) { + assert(parameters.map(_._1) == List( + "_byte", "_char", "_short", "_int", "_long", "_float", "_double", "_string", "_class")) + } + + parameters.sortBy(_._1) match { + case ("_byte", byte) :: ("_char", char) + :: ("_class", clasz) :: ("_double", double) + :: ("_float", float) :: ("_int", int) + :: ("_long", long) :: ("_short", short) + :: ("_string", string) :: Nil => + + assertMatch("byte", byte) { case Literal(Constant(1)) => } + assertMatch("char", char) { case Literal(Constant('2')) => } + assertMatch("short", short) { case Literal(Constant(3)) => } + assertMatch("int", int) { case Literal(Constant(4)) => } + assertMatch("long", long) { case Literal(Constant(5L)) => } + assertMatch("float", float) { case Literal(Constant(6.7f)) => } + assertMatch("double", double) { case Literal(Constant(8.9d)) => } + assertMatch("string", string) { case Literal(Constant("ten")) => } + assertMatch("class", clasz) { case Literal(Constant(ObjectTpe)) => } + } + + assert(nested.tree.tpe =:= typeOf[Nested_0]) + nested.tree.children match { + case _ :: inner :: Nil => + assertMatch("inner", inner) { + case AssignOrNamedArg(Ident(TermName("inner")), Apply(Select(New(tpe), nme.CONSTRUCTOR), AssignOrNamedArg(Ident(TermName("value")), Literal(Constant("turkey"))) :: Nil)) + if tpe.tpe =:= typeOf[Nested_0.Inner] => + } + } + + assert(arrays.tree.tpe =:= typeOf[Array_0.Repeated]) + arrays.tree.children match { + case _ :: AssignOrNamedArg(Ident(TermName("value")), Apply(arr, fst :: snd :: Nil)) :: Nil => + assertMatch("value(0)", fst) { + case Apply(Select(New(tpe), nme.CONSTRUCTOR), AssignOrNamedArg(Ident(TermName("value")), Apply(arr, args)) :: Nil) + if ((args zip Seq(8, 6, 7, 5, 3, 0, 9)) forall { case (Literal(Constant(l)), r) => l == r }) && + tpe.tpe =:= typeOf[Array_0] => + } + assertMatch("value(1)", snd) { + case Apply(Select(New(tpe), nme.CONSTRUCTOR), AssignOrNamedArg(Ident(TermName("value")), Apply(arr, args)) :: Nil) + if ((args zip Seq(6)) forall { case (Literal(Constant(l)), r) => l == r }) && + tpe.tpe =:= typeOf[Array_0] => + } + } + assert(enum.tree.tpe =:= typeOf[Enum_0]) + + assert(empty.tree.tpe =:= typeOf[Empty_0]) + assert(empty.tree.children.size == 1) + } + } + +} \ No newline at end of file diff --git a/test/files/run/t8928/Empty_0.java b/test/files/run/t8928/Empty_0.java new file mode 100644 index 000000000000..a5f6866c3d76 --- /dev/null +++ b/test/files/run/t8928/Empty_0.java @@ -0,0 +1,7 @@ +package test; + +import java.lang.annotation.*; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Empty_0 {} \ No newline at end of file diff --git a/test/files/run/t8928/Enum_0.java b/test/files/run/t8928/Enum_0.java new file mode 100644 index 000000000000..ab88e7ed63c8 --- /dev/null +++ b/test/files/run/t8928/Enum_0.java @@ -0,0 +1,12 @@ +package test; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Enum_0 { + Enum choice(); + + public enum Enum { + ONE, OTHER + } +} \ No newline at end of file diff --git a/test/files/run/t8928/Macros_0.scala b/test/files/run/t8928/Macros_0.scala new file mode 100644 index 000000000000..4940e3d60ab9 --- /dev/null +++ b/test/files/run/t8928/Macros_0.scala @@ -0,0 +1,16 @@ +package test + +object Macros { + import language.experimental.macros + def apply[T]: Unit = macro impl[T] + + import reflect.macros.whitebox + def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = { + import c.universe._ + + new Checks[c.universe.type](c.universe, ordered = true) + .check(weakTypeOf[T]) + + Literal(Constant(())) + } +} \ No newline at end of file diff --git a/test/files/run/t8928/Nested_0.java b/test/files/run/t8928/Nested_0.java new file mode 100644 index 000000000000..ea71d9b8cf06 --- /dev/null +++ b/test/files/run/t8928/Nested_0.java @@ -0,0 +1,13 @@ +package test; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Nested_0 { + Inner inner(); + + @Retention(RetentionPolicy.RUNTIME) + public @interface Inner { + String value(); + } +} \ No newline at end of file diff --git a/test/files/run/t8928/NoArgs_0.java b/test/files/run/t8928/NoArgs_0.java new file mode 100644 index 000000000000..e29b688c116b --- /dev/null +++ b/test/files/run/t8928/NoArgs_0.java @@ -0,0 +1,7 @@ +package test; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +public @interface NoArgs_0 {} + diff --git a/test/files/run/t8928/Simple_0.java b/test/files/run/t8928/Simple_0.java new file mode 100644 index 000000000000..b67c2bee241c --- /dev/null +++ b/test/files/run/t8928/Simple_0.java @@ -0,0 +1,18 @@ +package test; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Simple_0 { + byte _byte(); + char _char(); + short _short(); + int _int(); + long _long(); + float _float(); + double _double(); + String _string(); + Class _class(); + + short THREE = 3; +} \ No newline at end of file diff --git a/test/files/run/t8928/Test_1.scala b/test/files/run/t8928/Test_1.scala new file mode 100644 index 000000000000..1cef564ff1be --- /dev/null +++ b/test/files/run/t8928/Test_1.scala @@ -0,0 +1,16 @@ +import test._ + +object Test extends App { + Macros.apply[Annotated_0] + Macros.apply[Annotated_1] + + import reflect.runtime.universe + import universe._ + + /* consistency check with runtime universe */ + val checks = new Checks[universe.type](universe, ordered = false) + checks.check(weakTypeOf[Annotated_0]) + checks.check(weakTypeOf[Annotated_1]) + + +} \ No newline at end of file