在Scala编译器插件中,我正在尝试创建一个实现预先存在的特征的新类。到目前为止,我的代码看起来像这样:
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的“覆盖def变换(树:树):树”方法只能返回一个 Tree,对于它接收的每个Tree,所以它实际上不能生成一个新的Tree,只能修改一个。
那么,如何在现有包中添加新类?也许如果我在“transform(Tree)”得到它的时候改变了包,它会工作,但是我指出我还不知道包的内容,所以我不能在这个早期生成新类(或者我可以吗?) 。或者它可能与符号的“位置”参数有关?
到目前为止,我发现了几个修改树的示例,但没有在编译器插件中创建全新类的示例。
答案 0 :(得分:3)
完整的源代码在这里:https://gist.github.com/1794246
诀窍是存储新创建的ClassDef
并在创建新PackageDef
时使用它们。请注意,您需要处理符号和树:包符号只是一个句柄。为了生成代码,您需要生成一个AST(就像一个类,其中符号包含类名和类型,但代码在ClassDef
树中)。
正如您所指出的,包定义在树上比在类中更高,因此您需要先递归(假设您将从现有类生成新类)。然后,一旦遍历了子树,就可以使用新类准备一个新的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
}
}