如何在Scala编译器插件中添加一个新的类?
在Scala编译器插件中,我试图创build一个实现预先存在的特性的新类。 到目前为止,我的代码如下所示:
def trait2Impl(original: ClassDef, newName: String): ClassDef = { val impl = original.impl // Seems OK to have same self, but does not make sense to me ... val self = impl.self // TODO: implement methods ... val body = impl.body // We implement original val parents = original :: impl.parents val newImpl = treeCopy.Template(impl, parents, self, body) val name = newTypeName(newName) // We are a syntheic class, not a user-defined trait val mods = (original.mods | SYNTHETIC) &~ TRAIT val tp = original.tparams val result = treeCopy.ClassDef(original, mods, name, tp, newImpl) // Same Package? val owner = original.symbol.owner // New symbol. What's a Position good for? val symbol = new TypeSymbol(owner, NoPosition, name) result.setSymbol(symbol) symbol.setFlag(SYNTHETIC) symbol.setFlag(ABSTRACT) symbol.resetFlag(INTERFACE) symbol.resetFlag(TRAIT) owner.info.decls.enter(symbol) result }
但它似乎并没有被添加到包中。 我怀疑这是因为实际上,软件包在导致生成的特征之前被“遍历”,和/或因为TypingTransformer的“override def transform(tree:Tree):Tree”方法只能返回一棵树,它接收,所以它实际上不能产生一个新的树,但只能修改一个。
那么,如何将一个新的类添加到现有的包? 如果我在“transform(Tree)”得到它的时候转换了这个包,也许它会工作,但是我不知道这个包的内容,所以我不能在这个早期生成新的类(或者我可以吗? 。 或者,它可能与Symbol的“Position”参数有关?
到目前为止,我发现了几个树被修改的例子,但没有一个在Compiler Plugin中创build了一个全新的类。
完整的源代码在这里: https : //gist.github.com/1794246
诀窍是存储新创build的ClassDef
,并在创build新的PackageDef
时使用它们。 请注意,您需要处理符号和树:包符号只是一个句柄。 为了生成代码,您需要生成一个AST(就像一个类,其中的符号包含类名称和types,但代码位于ClassDef
树中)。
正如你所指出的,包的定义比类更高,所以你需要首先recursion(假设你将从现有的类中生成新的类)。 然后,一旦遍历了子树,就可以用新的类准备一个新的PackageDef(每个编译单元都有一个包定义,默认为空包)。
在这个例子中,假设源代码是
class Foo { def foo { "spring" } }
编译器把它包装进去
package <empty> { class Foo { def foo { "spring" } } }
和插件转换成
package <empty> { class Foo { def foo { "spring" } } package mypackage { class MyClass extends AnyRef } }