Skip to content

Commit

Permalink
Macro refactoring (#117) (#119)
Browse files Browse the repository at this point in the history
* Add test code failing on compilation with Scala.JS (#117)

* Comment about #117

* Workaround for #116

* Sanity refactor (see #92)

* Update CI config
  • Loading branch information
cchantep authored and gmethvin committed Oct 26, 2017
1 parent 158ae63 commit 1607ce8
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: scala
scala:
- 2.12.2
- 2.12.4
- 2.11.11
- 2.10.6
jdk:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@ import scala.reflect.macros.blackbox
@deprecated("Use implicitConfigReadsImpl or withOptionsReadsImpl", "2.6.6")
protected def readsImpl[A: c.WeakTypeTag, O: c.WeakTypeTag]: c.Expr[Reads[A]] = macroImpl[A, Reads, Reads](
implicitOptionsConfig, "read", "map", reads = true, writes = false)

@deprecated("Use implicitConfigWritesImpl or withOptionsWritesImpl", "2.6.6")
protected def writesImpl[A: c.WeakTypeTag, O: c.WeakTypeTag]: c.Expr[OWrites[A]] = macroImpl[A, OWrites, Writes](
implicitOptionsConfig, "write", "contramap", reads = false, writes = true)

@deprecated("Use implicitConfigFormatImpl or withOptionsFormatImpl", "2.6.6")
protected def formatImpl[A: c.WeakTypeTag, O: c.WeakTypeTag]: c.Expr[OFormat[A]] = macroImpl[A, OFormat, Format](
implicitOptionsConfig, "format", "inmap", reads = true, writes = true)
Expand Down Expand Up @@ -95,10 +97,7 @@ import scala.reflect.macros.blackbox
val libs = q"_root_.play.api.libs"
val json = q"$libs.json"
val syntax = q"$libs.functional.syntax"
val utilPkg = q"$json.util"
val JsPath = q"$json.JsPath"
val Reads = q"$json.Reads"
val Writes = q"$json.Writes"
val unlift = q"$syntax.unlift"
val atpe = atag.tpe.dealias

Expand Down Expand Up @@ -577,20 +576,26 @@ import scala.reflect.macros.blackbox
cq"""x: $t => {
val xjs = implicitly[Writes[$t]].writes(x)
@inline def jso = xjs match {
case xo @ JsObject(_) => xo
case jsv => JsObject(Seq("_value" -> jsv))
case xo @ $json.JsObject(_) => xo
case jsv => $json.JsObject(Seq("_value" -> jsv))
}

jso + ("_type" -> JsString(${typeNaming(t)}))
jso + ("_type" -> $json.JsString(${typeNaming(t)}))
}"""
})

// Use shadowing to eliminate the generated term itself
// from the implicit scope, due to the contravariant/implicit issue:
// https://groups.google.com/forum/#!topic/scala-language/ZE83TvSWpT4

val shadowName = TermName(term.name.decodedName.toString.trim)
// DO NOT directly use `term.name` as for some reason,
// the TermName is provided within Scala.JS is appended with a final ' '

q"""{ v: ${atpe} =>
val ${term.name.asInstanceOf[TermName]} = "eliminatedImplicit"
def ${shadowName}: $json.OWrites[${atpe}] =
sys.error("Invalid implicit resolution")

$cases
}"""
}
Expand Down
25 changes: 20 additions & 5 deletions play-json/shared/src/test/scala/play/api/libs/json/MacroSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class MacroSpec extends WordSpec with MustMatchers
}

"as Format for a simple generic case class" in {
val fmt = Json.format[Lorem[Double]]
val fmt: OFormat[Lorem[Double]] = Json.format

fmt.reads(Json.obj("ipsum" -> 0.123D, "age" -> 1)).get mustEqual Lorem(
0.123D, 1
Expand Down Expand Up @@ -104,8 +104,8 @@ class MacroSpec extends WordSpec with MustMatchers
implicit val simpleReads = Reads[Simple] { js =>
(js \ "bar").validate[String].map(Simple(_))
}
implicit val optionalReads: Reads[Optional] = Json.reads[Optional]
implicit val familyReads: Reads[Family] = Json.reads[Family]
implicit val optionalReads: Reads[Optional] = Json.reads
implicit val familyReads: Reads[Family] = Json.reads

val simple = Simple("foo")
val optional = Optional(None)
Expand Down Expand Up @@ -150,7 +150,7 @@ class MacroSpec extends WordSpec with MustMatchers
}

"as Format for a generic case class" in {
val fmt = Json.format[Lorem[Float]]
val fmt: Format[Lorem[Float]] = Json.format

fmt.writes(Lorem(2.34F, 2)) mustEqual Json.obj(
"ipsum" -> 2.34F, "age" -> 2
Expand Down Expand Up @@ -192,7 +192,12 @@ class MacroSpec extends WordSpec with MustMatchers
implicit val simpleWrites = Writes[Simple] { simple =>
Json.obj("bar" -> simple.bar)
}
implicit val optionalWrites: OWrites[Optional] = Json.writes[Optional]

implicit val optionalWrites = Json.writes[Optional]
// Following won't work due to inferrence issue (see #117)
// with inheritance/contravariance/implicit resolution
//val _: OWrites[Optional] = Json.writes

implicit val familyWrites: OWrites[Family] = Json.writes[Family]

val simple = Simple("foo")
Expand Down Expand Up @@ -529,6 +534,16 @@ class MacroSpec extends WordSpec with MustMatchers

sealed trait EmptyFamily

object FamilyCodec {
implicit val simpleWrites = Json.writes[Simple]
implicit val optionalWrites = Json.writes[Optional]

implicit val familyWrites = Json.writes[Family] // Failing:
/* java.lang.IllegalArgumentException:
requirement failed: familyWrites is not a valid identifier
*/
}

object Foo {
import shapeless.tag.@@

Expand Down

0 comments on commit 1607ce8

Please sign in to comment.