There is a library X I'm working on, which depends on another library Y. To support multiple versions of Y, X publishes multiple artifacts named X_Y1.0, X_Y1.1, etc. This is done using multiple subprojects in SBT with version-specific source directories like src/main/scala-Y1.0 and src/main/scala-Y1.1.
So far, it worked well. One minor problem is that sometimes version-specific source directories are too much. Sometimes they require a lot of code duplication because it's syntactically impossible to extract just the tiny differences into separate files. Sometimes doing so introduces performance overhead or makes the code unreadable.
Trying to solve the issue, I've added macro annotations to selectively delete a part of the code. It works like this:
class MyClass {
@UntilB1_0
def f: Int = 1
@SinceB1_1
def f: Int = 2
}
However, it seems it only works for methods. When I try to use the macro on fields, compilation fails with an error saying "f is already defined as value f". Also, it doesn't work for classes and objects.
My suspicion is that macros are applied during compilation before resolving method overloads, but after basic checks like checking duplicate names.
Is there a way to make the macros work for fields, classes, and objects too?
Here's an example macro to demonstrate the issue.
import scala.annotation.{compileTimeOnly, StaticAnnotation}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
@compileTimeOnly("enable macro paradise to expand macro annotations")
class Delete extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro DeleteMacro.impl
}
object DeleteMacro {
def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
c.Expr[Nothing](EmptyTree)
}
}
When the annotation @Delete is used on methods, it works.
class MyClass {
@Delete
def f: Int = 1
def f: Int = 2
}
// new MyClass().f == 2
However, it doesn't work for fields.
class MyClass {
@Delete
val f: Int = 1
val f: Int = 2
}
// error: f is already defined as value f