diff --git a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala index d91522c25f12..ab94fe60e3d8 100644 --- a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala @@ -6,6 +6,7 @@ import Symbols._, Types._, Contexts._, SymDenotations._, DenotTransformers._, Fl import util.Positions._ import SymUtils._ import StdNames._, NameOps._ +import Decorators._ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: Context) { import ast.tpd._ @@ -13,6 +14,10 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: val superCls: Symbol = cls.superClass val mixins: List[ClassSymbol] = cls.mixins + lazy val JUnit4Annotations: List[Symbol] = List("Test", "Ignore", "Before", "After", "BeforeClass", "AfterClass"). + map(n => ctx.getClassIfDefined("org.junit." + n)). + filter(_.exists) + def implementation(member: TermSymbol): TermSymbol = { val res = member.copy( owner = cls, @@ -59,10 +64,14 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: def needsDisambiguation = competingMethods.exists(x=> !(x is Deferred)) // multiple implementations are available def hasNonInterfaceDefinition = competingMethods.exists(!_.owner.is(Trait)) // there is a definition originating from class meth.is(Method, butNot = PrivateOrAccessorOrDeferred) && - (meth.owner.is(Scala2x) || needsDisambiguation || hasNonInterfaceDefinition ) && + (meth.owner.is(Scala2x) || needsDisambiguation || hasNonInterfaceDefinition || needsJUnit4Fix(meth) ) && isCurrent(meth) } + private def needsJUnit4Fix(meth: Symbol): Boolean = { + meth.annotations.nonEmpty && JUnit4Annotations.exists(annot => meth.hasAnnotation(annot)) + } + /** Get `sym` of the method that needs a forwarder * Method needs a forwarder in those cases: * - there is a trait that defines a primitive version of implemented polymorphic method. diff --git a/tests/run/junitForwarders/C_1.scala b/tests/run/junitForwarders/C_1.scala new file mode 100644 index 000000000000..6246e9436866 --- /dev/null +++ b/tests/run/junitForwarders/C_1.scala @@ -0,0 +1,15 @@ +trait T { + @org.junit.Test def foo = 0 +} + +class C extends T + +object Test extends App { + def check(c: Class[_], e: String) = { + val s = c.getDeclaredMethods.sortBy(_.getName).map(m => s"${m.getName} - ${m.getDeclaredAnnotations.mkString(", ")}").mkString(";") + assert(s == e, s"found: $s\nexpected: $e") + } + check(classOf[C], "foo - @org.junit.Test()") + // TODO scala-dev#213: should `foo$` really carry the @Test annotation? + check(classOf[T], "$init$ - ;foo - @org.junit.Test()") +} diff --git a/tests/run/junitForwarders/Test.java b/tests/run/junitForwarders/Test.java new file mode 100644 index 000000000000..57c4d5b544d8 --- /dev/null +++ b/tests/run/junitForwarders/Test.java @@ -0,0 +1,10 @@ +package org.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface Test { }