Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add a new Class in a Scala Compiler Plugin?

Tags:

scala

In a Scala Compiler Plugin, I'm trying to create a new class that implement a pre-existing trait. So far my code looks like this:

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
}

But it doesn't seem to get added to the package. I suspect that is because actually the package got "traversed" before the trait that causes the generation, and/or because the "override def transform(tree: Tree): Tree" method of the TypingTransformer can only return one Tree, for every Tree that it receives, so it cannot actually produce a new Tree, but only modify one.

So, how do you add a new Class to an existing package? Maybe it would work if I transformed the package when "transform(Tree)" gets it, but I that point I don't know the content of the package yet, so I cannot generate the new Class this early (or could I?). Or maybe it's related to the "Position" parameter of the Symbol?

So far I found several examples where Trees are modified, but none where a completely new Class is created in a Compiler Plugin.

like image 448
Sebastien Diot Avatar asked Oct 17 '11 18:10

Sebastien Diot


People also ask

What is plugin in Scala?

Introduction. A compiler plugin is a compiler component that lives in a separate JAR file from the main compiler. The compiler can then load that plugin and gain extra functionality. This tutorial briefly walks you through writing a plugin for the Scala compiler.

What is Scala Maven plugin?

The scala-maven-plugin (previously maven-scala-plugin) is used for compiling/testing/running/documenting scala code of any maven project.

Does gradle work with Scala?

Gradle supports version 1.6. 0 of Zinc and above. The Zinc compiler itself needs a compatible version of scala-library that may be different from the version required by your application. Gradle takes care of specifying a compatible version of scala-library for you.


1 Answers

The full source code is here: https://gist.github.com/1794246

The trick is to store the newly created ClassDefs and use them when creating a new PackageDef. Note that you need to deal with both Symbols and trees: a package symbol is just a handle. In order to generate code, you need to generate an AST (just like for a class, where the symbol holds the class name and type, but the code is in the ClassDef trees).

As you noted, package definitions are higher up the tree than classes, so you'd need to recurse first (assuming you'll generate the new class from an existing class). Then, once the subtrees are traversed, you can prepare a new PackageDef (every compilation unit has a package definition, which by default is the empty package) with the new classes.

In the example, assuming the source code is

class Foo {
  def foo {
    "spring"
  }
}

the compiler wraps it into

package <empty> {
  class Foo {
    def foo {
      "spring"
    }
  }
}

and the plugin transforms it into

package <empty> {
  class Foo {
    def foo {
      "spring"
    }
  }
  package mypackage {
    class MyClass extends AnyRef
  }
}
like image 112
Iulian Dragos Avatar answered Oct 17 '22 21:10

Iulian Dragos