0

So, I am new to macros in Scala and am having some difficulty with my current objective. I have a class that performs Json parsing of case classes. If the given case class has fields that are of type Enumeration, an implicit array of those enumerations needs to be provided when calling the parser.

I want to use a macro to verify the usages of the parser (at compile time), examine the class that is being sent to the parser and verify whether it has enums in it. If it does have enums, I want to verify that the implicit array of enums is not empty.

Here is what I have so far. I'd like to avoid c.eval if possible, so if there is a better way, I am all for it. I keep getting errors about "forgetting to splice a variable" without much more context than that. Am I even on the right track here?

object Macro {
  def compileTimeCheck(tpe: Any, enums: Array[Enumeration]) = macro impl
  def impl(c: Context)(tpe: c.Expr[Any], enums: c.Expr[Array[Enumeration]]) : c.Expr[Any] = {
    import c.universe._
    reify {
      if (c.eval(enums).isEmpty) {
        if (c.eval(tpe).getClass.getDeclaredFields.exists(_.getType.isInstanceOf[Enumeration])) {
          c.abort(c.macroApplication.pos,
            "You must provide an implicit list of enums in this case")
        }
      }
    }
  }
}
Jake Sankey
  • 4,977
  • 12
  • 39
  • 53
  • c.eval has proven to be quite unreliable, so I'd advise against using it (i.e. against designing macros that require evaluating stuff at compile time). – Eugene Burmako Aug 04 '15 at 05:57
  • Luckily, in your case it's relatively easy to come up with a macro that does what you need. Try changing your macro def signature to `def compileTimeCheck[T](enums: Array[...])` and your macro impl signature to `def impl[T: c.WeakTypeTag](c: Context)(enums: c.Expr[Array[...]])`. Then you'll be able to lookup the type that gets passed and check out its members - all without eval. – Eugene Burmako Aug 04 '15 at 05:59
  • Speaking of `enums`, try requiring that people pass an array literal into your macro, e.g. `compileTimeCheck[Something](Array(...))`. Then you can pattern match on the shape of the input argument using quasiquotes and make sure that they provided at least one element. – Eugene Burmako Aug 04 '15 at 06:00
  • If you want to use your macro like `compileTimeCheck[...](dynamicValue)`, where the value of the argument is only known at runtime (i.e. requires an eval), then you can't do that, because macros expand at compile time, so they can't know runtime values. – Eugene Burmako Aug 04 '15 at 06:01
  • @EugeneBurmako Thanks for the input. I was able to get it working with compileTimeCheck[SomeThing](Array(Enum))... Unfortunately, I really need to execute it as compileTimeCheck[A](enums).... Everytime we use our JsonMapper class to map an object, I would like it fail compilation if this criteria mentioned above is not met, if it is met, go ahead and compile and run. So JsonMapper.parse[SomeThing](jsonStr)(enums) should fail compilation if enums is empty and SomeThing does in fact contain enum fields. – Jake Sankey Aug 04 '15 at 21:43
  • I don't think that it's possible in general case, because `enums` might do arbitrary effects or even require information only available at production machines, not where the program is compiled. If you could restrict your goals, then it might be possible to come up with something, but in the current broad problem statement I doubt that. – Eugene Burmako Aug 05 '15 at 07:59

0 Answers0