1

I wrote a macro that parses JSON into a matching case class.

def parse(jsonTree: JsValue): BaseType = macro parserImpl

def parserImpl(c: blackbox.Context)(jsonTree: c.Tree) = {
  import c.universe._
  val q"$json" = jsonTree
  val cases = List("X", "Y").map { caseClassName =>
    val caseClass = c.parse(caseClassName)
    val reader = c.parse(s"JSONHelp.${caseClassName}_reads")
    val y = cq"""$caseClassName => (($json \ "config").validate[$caseClass]($reader)).get"""
    println(showCode(y))
    y
  }.toList

  val r =
    q"""
     import play.api.libs.json._
     import JSONHelp._
     println($json)
     ($json \ "type").as[String] match { case ..$cases }
    """
  println(showCode(r))

  r
}

The following is that code it generates (printed by the last println):

{

  import play.api.libs.json._;

  import JSONHelp._;

  println(NodeParser.this.json);

  NodeParser.this.json.\("type").as[String] match {

    case "X" => NodeParser.this.json.\("config").validate[X](JSONHelp.X_reads).get

    case "Y" => NodeParser.this.json.\("config").validate[Y](JSONHelp.Y_reads).get

  }

}

The compilation of the subproject containing the macro definition works fine. But when I compile the project(using sbt 0.13.11 and scala 2.11.8) using the macro, I get the following error:

java.lang.NullPointerException

at play.routes.compiler.RoutesCompiler$GeneratedSource$.unapply(RoutesCompiler.scala:37)

at play.sbt.routes.RoutesCompiler$$anonfun$11$$anonfun$apply$2.isDefinedAt(RoutesCompiler.scala:180)

at play.sbt.routes.RoutesCompiler$$anonfun$11$$anonfun$apply$2.isDefinedAt(RoutesCompiler.scala:179)

at scala.Option.collect(Option.scala:250)

at play.sbt.routes.RoutesCompiler$$anonfun$11.apply(RoutesCompiler.scala:179)

at play.sbt.routes.RoutesCompiler$$anonfun$11.apply(RoutesCompiler.scala:178)
Shivansh
  • 3,454
  • 23
  • 46
Mohit
  • 11
  • 1

1 Answers1

0

I'm not a user, but I see it seems to want tree positions with a source file:

  val routesPositionMapper: Position => Option[Position] = position => {
    position.sourceFile collect {
      case GeneratedSource(generatedSource) => {

It's typical to use atPos(pos)(tree). You might assume the incoming tree.pos for synthetic trees.

som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • I did not understand your comment. – Mohit Nov 16 '16 at 21:56
  • The snippet was based on your stack trace. Trees have positions, which are normally assigned by parser. If you create trees, you have to assign a position manually. `t.pos = c.macroApplication.pos` or similar. Positions matter for error reporting; and are sometimes used by macros to inspect source code. – som-snytt Nov 16 '16 at 23:29