summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@epfl.ch>2009-06-07 11:13:47 +0000
committerLukas Rytz <lukas.rytz@epfl.ch>2009-06-07 11:13:47 +0000
commit8cc477f8b6fd81c45fe30ac454c021a9769911ad (patch)
treece574d8e84ea47c453298fe557b49f7d51ddfdf3
parenta2166dec9d687347a08c9e6a95e07f1a67f0d370 (diff)
downloadscala-8cc477f8b6fd81c45fe30ac454c021a9769911ad.tar.gz
scala-8cc477f8b6fd81c45fe30ac454c021a9769911ad.tar.bz2
scala-8cc477f8b6fd81c45fe30ac454c021a9769911ad.zip
fixed BeanProperty, added BooleanBeanProperty, ...
fixed BeanProperty, added BooleanBeanProperty, added many tests (#1029, #1751, #294, #1942, #1782, #1788, #637).
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/StdNames.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala128
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala31
-rw-r--r--src/library/scala/reflect/BeanProperty.scala13
-rw-r--r--src/library/scala/reflect/BooleanBeanProperty.scala21
-rw-r--r--test/files/pos/annotations.scala50
-rw-r--r--test/files/pos/t1029/Test_1.scala7
-rw-r--r--test/files/pos/t1029/Test_2.scala3
-rw-r--r--test/files/pos/t1751/A1_2.scala2
-rw-r--r--test/files/pos/t1751/A2_1.scala2
-rw-r--r--test/files/pos/t1751/SuiteClasses.java3
-rw-r--r--test/files/pos/t1782/ImplementedBy.java3
-rw-r--r--test/files/pos/t1782/Test_1.scala10
-rw-r--r--test/files/pos/t1942/A_1.scala11
-rw-r--r--test/files/pos/t1942/Test_2.scala3
-rw-r--r--test/files/pos/t294/Ann.java3
-rw-r--r--test/files/pos/t294/Ann2.java3
-rw-r--r--test/files/pos/t294/Test_1.scala7
-rw-r--r--test/files/pos/t294/Test_2.scala1
20 files changed, 221 insertions, 84 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index b28cfc859c..ad0efdec11 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -346,6 +346,7 @@ trait Definitions {
lazy val SerializableAttr: Symbol = getClass("scala.serializable")
lazy val DeprecatedAttr: Symbol = getClass("scala.deprecated")
lazy val BeanPropertyAttr: Symbol = getClass(sn.BeanProperty)
+ lazy val BooleanBeanPropertyAttr: Symbol = getClass(sn.BooleanBeanProperty)
var AnnotationDefaultAttr: Symbol = _
lazy val NativeAttr: Symbol = getClass("scala.native")
lazy val VolatileAttr: Symbol = getClass("scala.volatile")
diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
index ae2f3a39c7..0f60d2796f 100644
--- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala
+++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
@@ -418,6 +418,7 @@ trait StdNames {
val ValueType : Name
val Serializable : Name
val BeanProperty : Name
+ val BooleanBeanProperty: Name
val Delegate : Name
val IOOBException: Name // IndexOutOfBoundsException
val Code : Name
@@ -465,6 +466,7 @@ trait StdNames {
final val ValueType = newTermName("System.ValueType")
final val Serializable = nme.NOSYMBOL
final val BeanProperty = nme.NOSYMBOL
+ final val BooleanBeanProperty = nme.NOSYMBOL
final val Delegate = newTermName("System.MulticastDelegate")
final val IOOBException = newTermName("System.IndexOutOfRangeException")
final val Code = nme.NOSYMBOL
@@ -486,6 +488,7 @@ trait StdNames {
private class J2SENames extends JavaNames {
final val Serializable = newTermName("java.io.Serializable")
final val BeanProperty = newTermName("scala.reflect.BeanProperty")
+ final val BooleanBeanProperty = newTermName("scala.reflect.BooleanBeanProperty")
final val Code = newTermName("scala.reflect.Code")
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index ef2fa32118..157263d9cc 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -372,18 +372,13 @@ trait Namers { self: Analyzer =>
else mods.flags & ~PRESUPER | STABLE)
if (nme.isSetterName(name))
context.error(tree.pos, "Names of vals or vars may not end in `_='")
- var getter = owner.newMethod(tree.pos, name).setFlag(accflags)
- setPrivateWithin(tree, getter, mods)
- getter = enterInScope(getter).asInstanceOf[TermSymbol]
- // needs the current context for adding synthetic BeanGetter / -Setter.
- // namerOf(getter) has a primaryConstructorContext which cannot be used
- // do add definitions to the class
- setInfo(getter)(namerOf(getter).getterTypeCompleter(vd, context))
+ // .isInstanceOf[..]: probably for (old) IDE hook. is this obsolete?
+ val getter = enterNewMethod(tree, name, accflags, mods).asInstanceOf[TermSymbol]
+ setInfo(getter)(namerOf(getter).getterTypeCompleter(vd))
if ((mods.flags & MUTABLE) != 0) {
- var setter = owner.newMethod(tree.pos, nme.getterToSetter(name))
- .setFlag(accflags & ~STABLE & ~CASEACCESSOR)
- setPrivateWithin(tree, setter, mods)
- setter = enterInScope(setter).asInstanceOf[TermSymbol]
+ val setter = enterNewMethod(tree, nme.getterToSetter(name),
+ accflags & ~STABLE & ~CASEACCESSOR,
+ mods).asInstanceOf[TermSymbol]
setInfo(setter)(namerOf(setter).setterTypeCompleter(vd))
}
tree.symbol =
@@ -403,6 +398,7 @@ trait Namers { self: Analyzer =>
vsym.setLazyAccessor(getter)
vsym
} else getter
+ addBeanGetterSetter(vd, getter)
}
case DefDef(mods, nme.CONSTRUCTOR, tparams, _, _, _) =>
var sym = owner.newConstructor(tree.pos).setFlag(mods.flags | owner.getFlag(ConstrFlags))
@@ -410,9 +406,7 @@ trait Namers { self: Analyzer =>
tree.symbol = enterInScope(sym)
finishWith(tparams)
case DefDef(mods, name, tparams, _, _, _) =>
- var sym = (owner.newMethod(tree.pos, name)).setFlag(mods.flags)
- setPrivateWithin(tree, sym, mods)
- tree.symbol = enterInScope(sym)
+ tree.symbol = enterNewMethod(tree, name, mods.flags, mods)
finishWith(tparams)
case TypeDef(mods, name, tparams, _) =>
var flags: Long = mods.flags
@@ -444,6 +438,58 @@ trait Namers { self: Analyzer =>
tree.symbol
}
+ def enterNewMethod(tree: Tree, name: Name, flags: Long, mods: Modifiers) = {
+ val sym = context.owner.newMethod(tree.pos, name).setFlag(flags)
+ setPrivateWithin(tree, sym, mods)
+ enterInScope(sym)
+ }
+
+ private def addBeanGetterSetter(vd: ValDef, getter: Symbol) {
+ def isAnn(ann: Tree, demand: String) = ann match {
+ case Apply(Select(New(Ident(name)), _), _) =>
+ name.toString == demand
+ case Apply(Select(New(Select(pre, name)), _), _) =>
+ name.toString == demand
+ case _ => false
+ }
+ val ValDef(mods, name, tpt, _) = vd
+ val hasBP = mods.annotations.exists(isAnn(_, "BeanProperty"))
+ val hasBoolBP = mods.annotations.exists(isAnn(_, "BooleanBeanProperty"))
+ if ((hasBP || hasBoolBP) && !forMSIL) {
+ if (!name(0).isLetter)
+ context.error(vd.pos, "`BeanProperty' annotation can be applied "+
+ "only to fields that start with a letter")
+ else if (mods hasFlag PRIVATE)
+ // avoids name clashes with private fields in traits
+ context.error(vd.pos, "`BeanProperty' annotation can only be applied "+
+ "to non-private fields")
+ else {
+ val flags = (mods.flags & (DEFERRED | OVERRIDE | STATIC)) | SYNTHETIC
+ val beanName = name(0).toString.toUpperCase + name.subName(1, name.length)
+
+ val getterName = if (hasBoolBP) "is" + beanName
+ else "get" + beanName
+ val getterMods = Modifiers(flags, mods.privateWithin,
+ mods.annotations map (_.duplicate))
+ val beanGetterDef = atPos(vd.pos) {
+ DefDef(getterMods, getterName, Nil, List(Nil), tpt.duplicate,
+ if (mods hasFlag DEFERRED) EmptyTree
+ else Select(This(getter.owner.name), name)) }
+ enterSyntheticSym(beanGetterDef)
+
+ if (mods hasFlag MUTABLE) {
+ // can't use "enterSyntheticSym", because the parameter type is not yet
+ // known. instead, uses the same machinery as for the non-bean setter:
+ // create and enter the symbol here, add the tree in Typer.addGettterSetter.
+ val setterName = "set" + beanName
+ val setter = enterNewMethod(vd, setterName, flags, mods).asInstanceOf[TermSymbol]
+ setInfo(setter)(namerOf(setter).setterTypeCompleter(vd))
+ }
+ }
+ }
+ }
+
+
// --- Lazy Type Assignment --------------------------------------------------
def typeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym =>
@@ -478,12 +524,10 @@ trait Namers { self: Analyzer =>
}
}
- def getterTypeCompleter(vd: ValDef, getterCtx: Context) = mkTypeCompleter(vd) { sym =>
+ def getterTypeCompleter(vd: ValDef) = mkTypeCompleter(vd) { sym =>
if (settings.debug.value) log("defining " + sym)
val tp = typeSig(vd)
sym.setInfo(PolyType(List(), tp))
- if (sym.hasAnnotation(BeanPropertyAttr) && sym.owner.isClass && !forMSIL)
- addBeanGetterSetter(vd, tp, getterCtx)
if (settings.debug.value) log("defined " + sym)
validate(sym)
}
@@ -496,56 +540,6 @@ trait Namers { self: Analyzer =>
validate(sym)
}
- private def addBeanGetterSetter(vd: ValDef, tp: Type, getterCtx: Context) {
- val ValDef(mods, name, _, _) = vd
- val sym = vd.symbol
- if (!name(0).isLetter)
- context.error(sym.pos, "`BeanProperty' annotation can be applied "+
- "only to fields that start with a letter")
- else {
- val tmplCtx = getterCtx.nextEnclosing(c => c.scope.toList.contains(sym))
- assert(tmplCtx != NoContext, context)
- val tmplNamer = newNamer(tmplCtx)
- val flags = (mods.flags & (DEFERRED | OVERRIDE | STATIC)) | SYNTHETIC
- val beanName = name(0).toString.toUpperCase + name.subName(1, name.length)
- val getterName = if (tp == BooleanClass.tpe) "is" + beanName
- else "get" + beanName
- val existingGetter = sym.owner.info.decl(getterName)
- if (existingGetter != NoSymbol) {
- if (!existingGetter.hasFlag(SYNTHETIC))
- context.error(sym.pos, "a defintion of `"+ getterName +
- "' already exists in "+ sym.owner)
- } else {
- val getterMods = Modifiers(flags, mods.privateWithin,
- mods.annotations map (_.duplicate))
- val beanGetterDef = atPos(sym.pos) {
- DefDef(getterMods, getterName, Nil, List(Nil), TypeTree(tp),
- if (mods hasFlag DEFERRED) EmptyTree
- else Select(This(sym.owner.name), name)) }
- tmplNamer.enterSyntheticSym(beanGetterDef)
- }
- if (mods hasFlag MUTABLE) {
- val setterName = "set" + beanName
- val existingSetter = sym.owner.info.decl(setterName)
- if (existingSetter != NoSymbol) {
- if (!existingSetter.hasFlag(SYNTHETIC))
- context.error(sym.pos, "a defintion of `"+ setterName +
- "' already exists in "+ sym.owner)
- } else {
- val setterMods = Modifiers(flags, mods.privateWithin,
- mods.annotations map (_.duplicate))
- val param = ValDef(NoMods, "new" + name, TypeTree(tp), EmptyTree)
- val beanSetterDef = atPos(sym.pos) {
- DefDef(setterMods, setterName, Nil, List(List(param)),
- TypeTree(UnitClass.tpe),
- if (mods hasFlag DEFERRED) EmptyTree
- else Assign(Select(This(sym.owner.name), name), Ident(param.name))) }
- tmplNamer.enterSyntheticSym(beanSetterDef)
- }
- }
- }
- }
-
def selfTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym =>
var selftpe = typer.typedType(tree).tpe
if (!(selftpe.typeSymbol isNonBottomSubClass sym.owner))
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 89cfcb21e2..4255fd54e2 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1271,22 +1271,33 @@ trait Typers { self: Analyzer =>
treeCopy.DefDef(result, result.mods, result.name,
result.tparams, result.vparamss, result.tpt, result.rhs)
}
- def setterDef: DefDef = {
- val setr = getter.setter(value.owner)
- setr.setAnnotations(value.annotations)
+ def setterDef(setter: Symbol): DefDef = {
+ setter.setAnnotations(value.annotations)
val result = atPos(vdef)(
- DefDef(setr,
- if ((mods hasFlag DEFERRED) || (setr hasFlag OVERLOADED))
+ DefDef(setter,
+ if ((mods hasFlag DEFERRED) || (setter hasFlag OVERLOADED))
EmptyTree
else
typed(Assign(Select(This(value.owner), value),
- Ident(setr.paramss.head.head)))))
+ Ident(setter.paramss.head.head)))))
treeCopy.DefDef(result, result.mods, result.name, result.tparams,
result.vparamss, result.tpt, result.rhs)
}
- val gs = if (mods hasFlag MUTABLE) List(getterDef, setterDef)
- else List(getterDef)
- if (mods hasFlag DEFERRED) gs else vdef :: gs
+
+ val gs = new ListBuffer[DefDef]
+ gs.append(getterDef)
+ if (mods hasFlag MUTABLE) {
+ val setter = getter.setter(value.owner)
+ gs.append(setterDef(setter))
+ if (!forMSIL && (value.hasAnnotation(BeanPropertyAttr) ||
+ value.hasAnnotation(BooleanBeanPropertyAttr))) {
+ val beanSetterName = "set" + name(0).toString.toUpperCase +
+ name.subName(1, name.length)
+ val beanSetter = value.owner.info.decl(beanSetterName)
+ gs.append(setterDef(beanSetter))
+ }
+ }
+ if (mods hasFlag DEFERRED) gs.toList else vdef :: gs.toList
}
case DocDef(comment, defn) =>
addGetterSetter(defn) map (stat => DocDef(comment, stat))
@@ -1798,7 +1809,7 @@ trait Typers { self: Analyzer =>
context.unit.synthetics get e.sym match {
case Some(tree) =>
newStats += typedStat(tree) // might add even more synthetics to the scope
- context.unit.synthetics -= e.sym
+ context.unit.synthetics -= e.sym
case _ =>
}
diff --git a/src/library/scala/reflect/BeanProperty.scala b/src/library/scala/reflect/BeanProperty.scala
index 1c75cb02fc..50c095486f 100644
--- a/src/library/scala/reflect/BeanProperty.scala
+++ b/src/library/scala/reflect/BeanProperty.scala
@@ -12,22 +12,21 @@
package scala.reflect
/** <p>
- * This annotation adds a setter and a getter method, following the
- * Java Bean convention (first letter of the property is capitalized)
- * used by popular Java web frameworks. For example:
+ * When attached to a field, this annotation adds a setter and a getter
+ * method following the Java Bean convention. For example:
* </p><pre>
* @BeanProperty
* <b>var</b> status = ""</pre>
* <p>
- * adds the following methods to the <b>generated</b> code
+ * adds the following methods to the class:
* </p><pre>
* <b>def</b> setStatus(s: String) { <b>this</b>.status = s }
* <b>def</b> getStatus: String = <b>this</b>.status
* </pre>
* <p>
- * However, you cannot call <code>setStatus</code> from
- * <a href="http://scala-lang.org/" target="_top">Scala</a>,
- * you should use the normal Scala access and assignment.
+ * For fields of type <code>Boolean</code>, if you need a getter
+ * named <code>isStatus</code>, use the
+ * <code>scala.reflect.BooleanBeanProperty</code> annotation instead.
* </p>
*/
class BeanProperty extends StaticAnnotation
diff --git a/src/library/scala/reflect/BooleanBeanProperty.scala b/src/library/scala/reflect/BooleanBeanProperty.scala
new file mode 100644
index 0000000000..cd9f2e5e9e
--- /dev/null
+++ b/src/library/scala/reflect/BooleanBeanProperty.scala
@@ -0,0 +1,21 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+// $Id: BeanProperty.scala 17925 2009-05-30 18:10:21Z rytz $
+
+
+package scala.reflect
+
+/** <p>
+ * This annotation has the same functionality as
+ * <code>scala.reflect.BeanProperty</code>, but the generated
+ * Bean getter will be named <code>isFieldName</code> instead
+ * of <code>getFieldName</code>.
+ * </p>
+ */
+class BooleanBeanProperty extends StaticAnnotation
diff --git a/test/files/pos/annotations.scala b/test/files/pos/annotations.scala
index 9f780c0094..5492d89351 100644
--- a/test/files/pos/annotations.scala
+++ b/test/files/pos/annotations.scala
@@ -22,5 +22,55 @@ object Test {
// bug #1070
trait T { @BeanProperty var field = 1 }
+
+ // annotation on annotation constructor
+ @(ann @ann(100))(200) def foo() = 300
+}
+
+// test forward references to getters / setters
+class BeanPropertyTests {
+ @scala.reflect.BeanProperty lazy val lv1 = 0
+
+ def foo() {
+ val bp1 = new BeanPropertyTests1
+
+ println(lv1)
+ println(getLv1())
+ println(bp1.getLv2())
+
+ println(getV1())
+ setV1(10)
+ bp1.setV2(100)
+ }
+
+ @scala.reflect.BeanProperty var v1 = 0
+
}
+class BeanPropertyTests1 {
+ @scala.reflect.BeanProperty lazy val lv2 = "0"
+ @scala.reflect.BeanProperty var v2 = 0
+}
+
+// test mixin of getters / setters, and implementing abstract
+// methods using @BeanProperty
+class C extends T with BeanF {
+ def foo() {
+ setF("doch!")
+ setG(true)
+ this.getF()
+ }
+}
+
+trait T {
+ @scala.reflect.BeanProperty var f = "nei"
+ @scala.reflect.BooleanBeanProperty var g = false
+}
+
+trait BeanF {
+ def getF(): String
+ def setF(n: String): Unit
+
+ def isG(): Boolean
+ def setG(nb: Boolean): Unit
+}
diff --git a/test/files/pos/t1029/Test_1.scala b/test/files/pos/t1029/Test_1.scala
new file mode 100644
index 0000000000..e828087f2c
--- /dev/null
+++ b/test/files/pos/t1029/Test_1.scala
@@ -0,0 +1,7 @@
+class ann(a: Array[Int]) extends StaticAnnotation
+
+object Test1 {
+ // bug #1029
+ @ann(Array(10, 2)) def u = ()
+ val v: String @ann(Array(13, 2)) = "-1"
+}
diff --git a/test/files/pos/t1029/Test_2.scala b/test/files/pos/t1029/Test_2.scala
new file mode 100644
index 0000000000..00589052cb
--- /dev/null
+++ b/test/files/pos/t1029/Test_2.scala
@@ -0,0 +1,3 @@
+object Test {
+ val t = Test1
+}
diff --git a/test/files/pos/t1751/A1_2.scala b/test/files/pos/t1751/A1_2.scala
new file mode 100644
index 0000000000..354d5eecd0
--- /dev/null
+++ b/test/files/pos/t1751/A1_2.scala
@@ -0,0 +1,2 @@
+@SuiteClasses(Array(classOf[A2]))
+class A1
diff --git a/test/files/pos/t1751/A2_1.scala b/test/files/pos/t1751/A2_1.scala
new file mode 100644
index 0000000000..c768062e43
--- /dev/null
+++ b/test/files/pos/t1751/A2_1.scala
@@ -0,0 +1,2 @@
+@SuiteClasses(Array())
+class A2
diff --git a/test/files/pos/t1751/SuiteClasses.java b/test/files/pos/t1751/SuiteClasses.java
new file mode 100644
index 0000000000..9f09021c5a
--- /dev/null
+++ b/test/files/pos/t1751/SuiteClasses.java
@@ -0,0 +1,3 @@
+public @interface SuiteClasses {
+ public Class<?>[] value();
+} \ No newline at end of file
diff --git a/test/files/pos/t1782/ImplementedBy.java b/test/files/pos/t1782/ImplementedBy.java
new file mode 100644
index 0000000000..6aa8b4fa9e
--- /dev/null
+++ b/test/files/pos/t1782/ImplementedBy.java
@@ -0,0 +1,3 @@
+public @interface ImplementedBy {
+ public Class<?> value();
+}
diff --git a/test/files/pos/t1782/Test_1.scala b/test/files/pos/t1782/Test_1.scala
new file mode 100644
index 0000000000..de4f0e9886
--- /dev/null
+++ b/test/files/pos/t1782/Test_1.scala
@@ -0,0 +1,10 @@
+@ImplementedBy(classOf[Provider])
+trait Service {
+ def someMethod()
+}
+
+class Provider
+ extends Service
+{
+ def someMethod() = ()
+}
diff --git a/test/files/pos/t1942/A_1.scala b/test/files/pos/t1942/A_1.scala
new file mode 100644
index 0000000000..19a7575a0a
--- /dev/null
+++ b/test/files/pos/t1942/A_1.scala
@@ -0,0 +1,11 @@
+class A {
+ def foo(x: Int) = 0
+ def foo(x: String) = 1
+}
+
+class ann(x: Int) extends StaticAnnotation
+
+class t {
+ val a = new A
+ @ann(a.foo(1)) def bar = 1
+}
diff --git a/test/files/pos/t1942/Test_2.scala b/test/files/pos/t1942/Test_2.scala
new file mode 100644
index 0000000000..6c045bbce5
--- /dev/null
+++ b/test/files/pos/t1942/Test_2.scala
@@ -0,0 +1,3 @@
+class Test {
+ println(new t)
+}
diff --git a/test/files/pos/t294/Ann.java b/test/files/pos/t294/Ann.java
new file mode 100644
index 0000000000..934ca46297
--- /dev/null
+++ b/test/files/pos/t294/Ann.java
@@ -0,0 +1,3 @@
+public @interface Ann {
+ public Ann2[] nested();
+}
diff --git a/test/files/pos/t294/Ann2.java b/test/files/pos/t294/Ann2.java
new file mode 100644
index 0000000000..958cf1ab76
--- /dev/null
+++ b/test/files/pos/t294/Ann2.java
@@ -0,0 +1,3 @@
+public @interface Ann2 {
+ public int value();
+} \ No newline at end of file
diff --git a/test/files/pos/t294/Test_1.scala b/test/files/pos/t294/Test_1.scala
new file mode 100644
index 0000000000..ff1f34b10e
--- /dev/null
+++ b/test/files/pos/t294/Test_1.scala
@@ -0,0 +1,7 @@
+// also test pickling of java annotations; Test_2.scala will
+// read this class file
+@Ann(nested = Array(new Ann2(10))) class Test {
+ @Ann2(100) var ctx: Object = _
+ @Ann(nested = Array()) def foo = 10
+ @Ann(nested = Array(new Ann2(10), new Ann2(23))) val bam = -3
+}
diff --git a/test/files/pos/t294/Test_2.scala b/test/files/pos/t294/Test_2.scala
new file mode 100644
index 0000000000..9fb1c6e175
--- /dev/null
+++ b/test/files/pos/t294/Test_2.scala
@@ -0,0 +1 @@
+class Test2 extends Test