如何在Scala编译器插件中添加新类?

时间:2011-10-17 18:11:50

标签: scala

在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)”得到它的时候改变了包,它会工作,但是我指出我还不知道包的内容,所以我不能在这个早期生成新类(或者我可以吗?) 。或者它可能与符号的“位置”参数有关?

到目前为止,我发现了几个修改树的示例,但没有在编译器插件中创建全新类的示例。

1 个答案:

答案 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
  }
}