我在Scala中创建了一个创建伴侣类的注释(如果它还不存在)并在伴侣类中添加了一些方法。 当我有一个注释时它工作正常,但是当我在类上放置两个注释时,不会生成伴随类($ file)的类文件。 此外,在已经有伴随对象的类上有两个注释也可以使用o_O。
以下是我的注释宏:
class Foo extends StaticAnnotation{
def macroTransform(annottees: Any*): Any = macro Foo.impl
}
object Foo {
def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
def addFooFunctionality(module: Tree): Tree = {
val fooerType = weakTypeOf[Fooer].typeSymbol
module match {
case q"object $className { ..$body }" =>
q"""
object $className extends $fooerType {
..$body
val doFoo: String = "Foo"
}
"""
case q"object $className extends ..$parents { ..$body }" =>
q"""
object $className extends ..$parents with $fooerType {
..$body
val doFoo: String = "Foo"
}
"""
case _ => c.abort(c.enclosingPosition, "Module does not match")
}
}
val result = annottees map (_.tree) toList match {
case (classDef @ ClassDef(cMods, cName, _, _)) :: tail =>
val moduleDef = tail match {
case (md @ ModuleDef(_, mName, _)) :: Nil if cName.decodedName.toString == mName.decodedName.toString => md
case Nil =>
q"""
object ${cName.toTermName} {
}
"""
case e => c.abort(c.enclosingPosition, s"Foo annotation only works on classes. Not $e.")
}
val modifiedModuleDef = addFooFunctionality(moduleDef)
c.Expr[Fooer](q"""
$classDef
$modifiedModuleDef
""")
case e => c.abort(c.enclosingPosition, s"Foo annotation only works on classes. Not $e.")
}
result
}
}
trait Fooer {
val doFoo : String
}
和Bar class Bar扩展StaticAnnotation { def macroTransform(annottees:Any *):Any = macro Bar.impl }
object Bar {
def impl(c: scala.reflect.macros.whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
def addBarFunctionality(module: Tree): Tree = {
val barerType = weakTypeOf[Barer].typeSymbol
module match {
case q"object $className { ..$body }" =>
q"""
object $className extends $barerType {
..$body
val doBar: String = "Bar"
}
"""
case q"object $className extends ..$parents { ..$body }" =>
q"""
object $className extends ..$parents with $barerType {
..$body
val doBar: String = "Bar"
}
"""
}
}
annottees map (_.tree) toList match {
case (classDef @ ClassDef(cMods, cName, _, _)) :: tail =>
val moduleDef = tail match {
case (md @ ModuleDef(_, mName, _)) :: Nil if cName.decodedName.toString == mName.decodedName.toString => md
case Nil =>
q"""
object ${cName.toTermName} {
}
"""
case e => c.abort(c.enclosingPosition, s"Bar annotation only works on classes. Not $e.")
}
val modifiedModuleDef = addBarFunctionality(moduleDef)
c.Expr(q"""
$classDef
$modifiedModuleDef
""")
case e => c.abort(c.enclosingPosition, s"Bar annotation only works on classes. Not $e.")
}
}
}
trait Barer {
val doBar : String
}
我已经定义了这六个类
@Foo
case class A(name: String)
@Bar
case class B(name: String)
@Foo
@Bar
case class C(name: String)
@Bar
@Foo
case class D(name: String)
@Foo
@Bar
case class E(name: String)
object E {}
@Bar
@Foo
case class F(name: String)
object F {}
当我运行这些测试时
class FooBarTest {
@Test
def testA() = {
val a = A("Hello")
assertEquals("Hello", a.name)
assertEquals("Foo", A.doFoo)
}
@Test
def testB() = {
val b = B("Hello")
assertEquals("Hello", b.name)
assertEquals("Bar", B.doBar)
}
@Test
def testC() = {
val c = C("Hello")
assertEquals("Hello", c.name)
assertEquals("Foo", C.doFoo)
assertEquals("Bar", C.doBar)
}
@Test
def testD() = {
val d = D("Hello")
assertEquals("Hello", d.name)
assertEquals("Foo", D.doFoo)
assertEquals("Bar", D.doBar)
}
@Test
def testE() = {
val e = E("Hello")
assertEquals("Hello", e.name)
assertEquals("Foo", E.doFoo)
assertEquals("Bar", E.doBar)
}
@Test
def testF() = {
val f = F("Hello")
assertEquals("Hello", f.name)
assertEquals("Foo", F.doFoo)
assertEquals("Bar", F.doBar)
}
}
testA
,testB
,testE
,testF
通过。
testC
和testD
失败
java.lang.NoClassDefFoundError: gizmo/C$
at gizmo.FooBarTest.testC(FooBarTest.scala:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.ClassNotFoundException: gizmo.C$
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 28 more
testD的相同消息。