aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Pretty <jon.pretty@propensive.com>2017-11-30 13:47:15 +0100
committerGitHub <noreply@github.com>2017-11-30 13:47:15 +0100
commitc698d7db4b6e89392b8ae399333a89b167db20c7 (patch)
treebf4a34e519dec8992ded3f92b7ec4acec08c1826
parent834af65ed610d6b1b278b5204801306808a90645 (diff)
parente2191e2687b671d4a8610544251e7e807f2793da (diff)
downloadmagnolia-c698d7db4b6e89392b8ae399333a89b167db20c7.tar.gz
magnolia-c698d7db4b6e89392b8ae399333a89b167db20c7.tar.bz2
magnolia-c698d7db4b6e89392b8ae399333a89b167db20c7.zip
Merge branch 'master' into patch-1
-rw-r--r--.gitignore2
-rw-r--r--CONTRIBUTORS2
-rwxr-xr-xbenchmarks/2.12/benchmark10
-rw-r--r--benchmarks/2.12/src/adt1.scala78
-rw-r--r--benchmarks/2.12/src/magnolia/show_50.scala100
-rw-r--r--benchmarks/2.12/src/scalaz-deriving/show_50.scala61
-rwxr-xr-xbenchmarks/fetch.sh8
-rw-r--r--build.sbt7
-rw-r--r--core/shared/src/main/scala/interface.scala13
-rw-r--r--core/shared/src/main/scala/magnolia.scala69
-rw-r--r--examples/shared/src/main/scala/default.scala3
-rw-r--r--examples/shared/src/main/scala/show.scala6
-rw-r--r--tests/src/main/scala/tests.scala45
13 files changed, 322 insertions, 82 deletions
diff --git a/.gitignore b/.gitignore
index ffa0d68..63d54ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,4 @@ target
.jvm
.js
.native
-.idea/
+.idea
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index f495d8d..e376ca4 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1,3 +1,3 @@
Jon Pretty <jon.pretty@propensive.com> [@propensive](https://twitter.com/propensive/)
Loïc Descotte
-
+Georgi Krastev <jorokr21@gmail.com> [@joro_kr](https://twitter.com/joro_kr/)
diff --git a/benchmarks/2.12/benchmark b/benchmarks/2.12/benchmark
index 744639d..513e7bd 100755
--- a/benchmarks/2.12/benchmark
+++ b/benchmarks/2.12/benchmark
@@ -6,17 +6,19 @@ shift
benchmark() {
DERIVATION="$1"
TEST="$2"
+ ADT="$3"
echo "$DERIVATION/$TEST:"
- echo "src/adt.scala" > .script
+ echo "src/$ADT.scala" > .script
for I in $(seq 1 $COUNT); do
echo "src/$DERIVATION/$TEST.scala" >> .script
done
- cat .script | scalac-2.12 -Xresident -d bin -cp $(ls lib/* | tr '\n' :)bin | ts -i -s '%M:%.S' > out.log
+ cat .script | scalac -Xplugin:lib/paradise.jar -Ypartial-unification -Xresident -d bin -cp $(ls lib/* | tr '\n' :)bin | ts -i -s '%M:%.S' > out.log
cat out.log | tail -n +2 | paste -d' ' - .script | sed 's/nsc.//g' | head -n -1
echo
}
for J in "$@"; do
- benchmark magnolia "$J"
- benchmark kittens "$J"
+ benchmark magnolia "$J" "adt"
+ benchmark kittens "$J" "adt"
+ benchmark scalaz-deriving "$J" "adt1"
done
diff --git a/benchmarks/2.12/src/adt1.scala b/benchmarks/2.12/src/adt1.scala
new file mode 100644
index 0000000..f9ab65f
--- /dev/null
+++ b/benchmarks/2.12/src/adt1.scala
@@ -0,0 +1,78 @@
+package adt1
+
+import scalaz._
+import Scalaz._
+
+// @deriving(Show, Equal) sealed trait Tree
+// @deriving(Show, Equal) case class Leaf(value: String) extends Tree
+// @deriving(Show, Equal) case class Branch(left: Tree, right: Tree) extends Tree
+
+// @deriving(Show, Equal) sealed trait GTree[T]
+// @deriving(Show, Equal) case class GLeaf[T](value: String) extends GTree[T]
+// @deriving(Show, Equal) case class GBranch[T](left: GTree[T], right: GTree[T]) extends GTree[T]
+
+// @deriving(Show, Equal) sealed trait Entity
+
+// @deriving(Show, Equal) case class Company(name: String) extends Entity
+// @deriving(Show, Equal) case class Human(name: String, age: Int) extends Entity
+// @deriving(Show, Equal) case class Address(line1: String, occupant: Human)
+
+@deriving(Show, Equal) sealed trait Alphabet
+
+@deriving(Show, Equal) case class Greek(άλφα: Letter,
+ βήτα: Letter,
+ γάμα: Letter,
+ δέλτα: Letter,
+ έψιλον: Letter,
+ ζήτα: Letter,
+ ήτα: Letter,
+ θήτα: Letter)
+ extends Alphabet
+
+@deriving(Show, Equal) case class Cyrillic(б: Letter, в: Letter, г: Letter, д: Letter, ж: Letter, з: Letter)
+ extends Alphabet
+
+@deriving(Show, Equal) case class Latin(a: Letter,
+ b: Letter,
+ c: Letter,
+ d: Letter,
+ e: Letter,
+ f: Letter,
+ g: Letter,
+ h: Letter,
+ i: Letter,
+ j: Letter,
+ k: Letter,
+ l: Letter,
+ m: Letter,
+ n: Letter,
+ o: Letter,
+ p: Letter,
+ q: Letter,
+ r: Letter,
+ s: Letter,
+ t: Letter,
+ u: Letter,
+ v: Letter)
+ extends Alphabet
+
+//@deriving(Show, Equal) case class Letter(name: String, phonetic: String)
+//@deriving(Show, Equal) case class Country(name: String, language: Language, leader: Person, existence: DateRange)
+//@deriving(Show, Equal) case class Language(name: String, code: String, alphabet: Alphabet)
+//@deriving(Show, Equal) case class Person(name: String, dateOfBirth: Date)
+//@deriving(Show, Equal) case class Date(year: Int, month: Month, day: Int)
+//@deriving(Show, Equal) case class DateRange(from: Date, toDate: Date)
+
+// @deriving(Show, Equal) sealed trait Month
+// @deriving(Show, Equal) case object Jan extends Month
+// @deriving(Show, Equal) case object Feb extends Month
+// @deriving(Show, Equal) case object Mar extends Month
+// @deriving(Show, Equal) case object Apr extends Month
+// @deriving(Show, Equal) case object May extends Month
+// @deriving(Show, Equal) case object Jun extends Month
+// @deriving(Show, Equal) case object Jul extends Month
+// @deriving(Show, Equal) case object Aug extends Month
+// @deriving(Show, Equal) case object Sep extends Month
+// @deriving(Show, Equal) case object Oct extends Month
+// @deriving(Show, Equal) case object Nov extends Month
+// @deriving(Show, Equal) case object Dec extends Month
diff --git a/benchmarks/2.12/src/magnolia/show_50.scala b/benchmarks/2.12/src/magnolia/show_50.scala
index 27cfbae..df431af 100644
--- a/benchmarks/2.12/src/magnolia/show_50.scala
+++ b/benchmarks/2.12/src/magnolia/show_50.scala
@@ -1,58 +1,58 @@
import magnolia._, examples._
object Gen {
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
- Show.generic[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
+ Show.gen[adt.Alphabet]
}
diff --git a/benchmarks/2.12/src/scalaz-deriving/show_50.scala b/benchmarks/2.12/src/scalaz-deriving/show_50.scala
new file mode 100644
index 0000000..37a00f0
--- /dev/null
+++ b/benchmarks/2.12/src/scalaz-deriving/show_50.scala
@@ -0,0 +1,61 @@
+import adt1._
+
+import scalaz._
+import Scalaz._
+
+object Gen {
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+ Show[Alphabet]
+}
diff --git a/benchmarks/fetch.sh b/benchmarks/fetch.sh
index 6644a06..4f7105d 100755
--- a/benchmarks/fetch.sh
+++ b/benchmarks/fetch.sh
@@ -10,4 +10,12 @@ for V in 2.12 2.11; do
wget -O $V/lib/cats-kernel.jar "http://repo1.maven.org/maven2/org/typelevel/cats-kernel_$V/1.0.0-RC1/cats-kernel_$V-1.0.0-RC1.jar"
wget -O $V/lib/cats-core.jar "http://repo1.maven.org/maven2/org/typelevel/cats-core_$V/1.0.0-RC1/cats-core_$V-1.0.0-RC1.jar"
wget -O $V/lib/kittens.jar "http://repo1.maven.org/maven2/org/typelevel/kittens_$V/1.0.0-RC1/kittens_$V-1.0.0-RC1.jar"
+
+ wget -O $V/lib/iotaz.jar "https://repo1.maven.org/maven2/io/frees/iotaz-core_$V/0.3.2/iotaz-core_$V-0.3.2.jar"
+ wget -O $V/lib/scalaz.jar "https://repo1.maven.org/maven2/org/scalaz/scalaz-core_$V/7.2.16/scalaz-core_$V-7.2.16.jar"
+ wget -O $V/lib/deriving-macro.jar "https://repo1.maven.org/maven2/com/fommil/deriving-macro_$V/0.9.0/deriving-macro_$V-0.9.0.jar"
+ wget -O $V/lib/paradise.jar "https://repo1.maven.org/maven2/org/scalamacros/paradise_$V.4/2.1.1/paradise_$V.4-2.1.1.jar"
+ wget -O $V/lib/scalaz-deriving.jar "https://repo1.maven.org/maven2/com/fommil/scalaz-deriving_$V/0.9.0/scalaz-deriving_$V-0.9.0.jar"
+ wget -O $V/lib/scalaz-deriving-core.jar "https://repo1.maven.org/maven2/com/fommil/scalaz-deriving-base_$V/0.9.0/scalaz-deriving-base_$V-0.9.0.jar"
+
done
diff --git a/build.sbt b/build.sbt
index f127515..178ac83 100644
--- a/build.sbt
+++ b/build.sbt
@@ -25,6 +25,13 @@ lazy val tests = project
.settings(buildSettings: _*)
.settings(unmanagedSettings)
.settings(moduleName := "magnolia-tests")
+ .settings(
+ addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full),
+ libraryDependencies ++= Seq(
+ "com.fommil" %% "deriving-macro" % "0.9.0",
+ "com.fommil" %% "scalaz-deriving" % "0.9.0"
+ )
+ )
.dependsOn(examplesJVM)
lazy val benchmarks = project
diff --git a/core/shared/src/main/scala/interface.scala b/core/shared/src/main/scala/interface.scala
index 54f8ce3..193a6f9 100644
--- a/core/shared/src/main/scala/interface.scala
+++ b/core/shared/src/main/scala/interface.scala
@@ -34,7 +34,7 @@ trait Param[Typeclass[_], Type] {
/** the type of the parameter being represented
*
- * For exmaple, for a case class,
+ * For example, for a case class,
* <pre>
* case class Person(name: String, age: Int)
* </pre>
@@ -46,6 +46,17 @@ trait Param[Typeclass[_], Type] {
/** the name of the parameter */
def label: String
+ /** flag indicating a repeated (aka. vararg) parameter
+ *
+ * For example, for a case class,
+ * <pre>
+ * case class Account(id: String, emails: String*)
+ * </pre>
+ * the [[Param]] instance corresponding to the `emails` parameter would be `repeated` and have a
+ * [[PType]] equal to the type `Seq[String]`. Note that only the last parameter of a case class
+ * can be repeated. */
+ def repeated: Boolean
+
/** the typeclass instance associated with this parameter
*
* This is the instance of the type `Typeclass[PType]` which will have been discovered by
diff --git a/core/shared/src/main/scala/magnolia.scala b/core/shared/src/main/scala/magnolia.scala
index 2431e4f..e8350da 100644
--- a/core/shared/src/main/scala/magnolia.scala
+++ b/core/shared/src/main/scala/magnolia.scala
@@ -65,11 +65,13 @@ object Magnolia {
* */
def gen[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
- import scala.util.{Try, Success, Failure}
val magnoliaPkg = q"_root_.magnolia"
val scalaPkg = q"_root_.scala"
+ val repeatedParamClass = definitions.RepeatedParamClass
+ val scalaSeqType = typeOf[Seq[_]].typeConstructor
+
val prefixType = c.prefix.tree.tpe
def companionRef(tpe: Type): Tree = {
@@ -95,7 +97,7 @@ object Magnolia {
"magnolia: the derivation object does not define the Typeclass type constructor")
}
- def checkMethod(termName: String, category: String, expected: String) = {
+ def checkMethod(termName: String, category: String, expected: String): Unit = {
val term = TermName(termName)
val combineClass = c.prefix.tree.tpe.baseClasses
.find { cls =>
@@ -204,9 +206,9 @@ object Magnolia {
val assignedName: TermName = TermName(c.freshName(s"${genericTypeName}Typeclass"))
val typeSymbol = genericType.typeSymbol
val classType = if (typeSymbol.isClass) Some(typeSymbol.asClass) else None
- val isCaseClass = classType.map(_.isCaseClass).getOrElse(false)
- val isCaseObject = classType.map(_.isModuleClass).getOrElse(false)
- val isSealedTrait = classType.map(_.isSealed).getOrElse(false)
+ val isCaseClass = classType.exists(_.isCaseClass)
+ val isCaseObject = classType.exists(_.isModuleClass)
+ val isSealedTrait = classType.exists(_.isSealed)
val primitives = Set(typeOf[Double],
typeOf[Float],
@@ -215,14 +217,14 @@ object Magnolia {
typeOf[Int],
typeOf[Long],
typeOf[Char],
- typeOf[Boolean])
+ typeOf[Boolean],
+ typeOf[Unit])
val isValueClass = genericType <:< typeOf[AnyVal] && !primitives.exists(_ =:= genericType)
val resultType = appliedType(typeConstructor, genericType)
val result = if (isCaseObject) {
- // FIXME: look for an alternative which isn't deprecated on Scala 2.12+
val obj = companionRef(genericType)
val className = genericType.typeSymbol.name.decodedName.toString
@@ -240,20 +242,29 @@ object Magnolia {
val className = genericType.typeSymbol.name.decodedName.toString
case class CaseParam(sym: c.universe.MethodSymbol,
+ repeated: Boolean,
typeclass: c.Tree,
paramType: c.Type,
ref: c.TermName)
- val caseParamsReversed: List[CaseParam] = caseClassParameters.foldLeft(List[CaseParam]()) {
- case (acc, param) =>
+ val caseParamsReversed = caseClassParameters.foldLeft[List[CaseParam]](Nil) {
+ (acc, param) =>
val paramName = param.name.decodedName.toString
- val paramType = param.returnType.substituteTypes(genericType.etaExpand.typeParams,
- genericType.typeArgs)
+ val paramTypeSubstituted = param.returnType.substituteTypes(
+ genericType.etaExpand.typeParams,
+ genericType.typeArgs)
+
+ val (repeated, paramType) = paramTypeSubstituted match {
+ case TypeRef(_, `repeatedParamClass`, typeArgs) =>
+ true -> appliedType(scalaSeqType, typeArgs)
+ case tpe =>
+ false -> tpe
+ }
val predefinedRef = acc.find(_.paramType == paramType)
val caseParamOpt = predefinedRef.map { backRef =>
- CaseParam(param, q"()", paramType, backRef.ref) :: acc
+ CaseParam(param, repeated, q"()", paramType, backRef.ref) :: acc
}
caseParamOpt.getOrElse {
@@ -266,7 +277,7 @@ object Magnolia {
val ref = TermName(c.freshName("paramTypeclass"))
val assigned = q"""val $ref = $derivedImplicit"""
- CaseParam(param, assigned, paramType, ref) :: acc
+ CaseParam(param, repeated, assigned, paramType, ref) :: acc
}
}
@@ -278,6 +289,7 @@ object Magnolia {
val preAssignments = caseParams.map(_.typeclass)
val defaults = if (!isValueClass) {
+
val constructorParams = genericType.decls.collect {
case a: MethodSymbol if a.isConstructor => a
}.head.paramLists.head.map(_.asTerm)
@@ -287,10 +299,17 @@ object Magnolia {
constructorParams.map(_ => q"$scalaPkg.None")
} else {
val caseClassCompanion = genericType.companion
- val constructorMethod = caseClassCompanion.decl(TermName("apply")).asMethod
- val indexedConstructorParams =
- constructorMethod.paramLists.head.map(_.asTerm).zipWithIndex
+ // If a companion object is defined with alternative apply methods
+ // it is needed get all the alternatives
+ val constructorMethods =
+ caseClassCompanion.decl(TermName("apply")).alternatives.map(_.asMethod)
+
+ // The last apply method in the alternatives is the one that belongs
+ // to the case class, not the user defined companion object
+ val indexedConstructorParams =
+ constructorMethods.last.paramLists.head.map(_.asTerm).zipWithIndex
+
indexedConstructorParams.map {
case (p, idx) =>
if (p.isParamWithDefault) {
@@ -298,14 +317,15 @@ object Magnolia {
q"$scalaPkg.Some(${genericType.typeSymbol.companion.asTerm}.$method)"
} else q"$scalaPkg.None"
}
+
}
} else List(q"$scalaPkg.None")
val assignments = caseParams.zip(defaults).zipWithIndex.map {
- case ((CaseParam(param, typeclass, paramType, ref), defaultVal), idx) =>
+ case ((CaseParam(param, repeated, typeclass, paramType, ref), defaultVal), idx) =>
q"""$paramsVal($idx) = $magnoliaPkg.Magnolia.param[$typeConstructor, $genericType,
$paramType](
- ${param.name.decodedName.toString}, $ref, $defaultVal, _.${param.name}
+ ${param.name.decodedName.toString}, $repeated, $ref, $defaultVal, _.${param.name}
)"""
}
@@ -326,7 +346,8 @@ object Magnolia {
($fnVal: $magnoliaPkg.Param[$typeConstructor, $genericType] => Any) =>
new $genericType(..${caseParams.zipWithIndex.map {
case (typeclass, idx) =>
- q"$fnVal($paramsVal($idx)).asInstanceOf[${typeclass.paramType}]"
+ val arg = q"$fnVal($paramsVal($idx)).asInstanceOf[${typeclass.paramType}]"
+ if (typeclass.repeated) q"$arg: _*" else arg
}})
))
}"""
@@ -399,9 +420,9 @@ object Magnolia {
val genericType: Type = weakTypeOf[T]
val currentStack: Stack =
- recursionStack.get(c.enclosingPosition).getOrElse(Stack(Map(), List(), List()))
+ recursionStack.getOrElse(c.enclosingPosition, Stack(Map(), List(), List()))
- val directlyReentrant = Some(genericType) == currentStack.frames.headOption.map(_.genericType)
+ val directlyReentrant = currentStack.frames.headOption.exists(_.genericType == genericType)
if (directlyReentrant) throw DirectlyReentrantException()
@@ -410,14 +431,14 @@ object Magnolia {
emittedErrors += error
val trace = error.path.mkString("\n in ", "\n in ", "\n \n")
- val msg = s"magnolia: could not derive ${typeConstructor} instance for type " +
+ val msg = s"magnolia: could not derive $typeConstructor instance for type " +
s"${error.genericType}"
c.info(c.enclosingPosition, msg + trace, true)
}
}
- val result: Option[Tree] = if (!currentStack.frames.isEmpty) {
+ val result: Option[Tree] = if (currentStack.frames.nonEmpty) {
findType(genericType) match {
case None =>
directInferImplicit(genericType, typeConstructor).map(_.tree)
@@ -459,11 +480,13 @@ object Magnolia {
* This method is intended to be called only from code generated by the Magnolia macro, and
* should not be called directly from users' code. */
def param[Tc[_], T, P](name: String,
+ isRepeated: Boolean,
typeclassParam: Tc[P],
defaultVal: => Option[P],
deref: T => P) = new Param[Tc, T] {
type PType = P
def label: String = name
+ def repeated: Boolean = isRepeated
def default: Option[PType] = defaultVal
def typeclass: Tc[PType] = typeclassParam
def dereference(t: T): PType = deref(t)
diff --git a/examples/shared/src/main/scala/default.scala b/examples/shared/src/main/scala/default.scala
index 4c1b634..4b781a5 100644
--- a/examples/shared/src/main/scala/default.scala
+++ b/examples/shared/src/main/scala/default.scala
@@ -30,6 +30,9 @@ object Default {
/** default value for ints; 0 */
implicit val int: Default[Int] = new Default[Int] { def default = 0 }
+ /** default value for sequences; the empty sequence */
+ implicit def seq[A]: Default[Seq[A]] = new Typeclass[Seq[A]] { def default = Seq.empty }
+
/** generates default instances of [[Default]] for case classes and sealed traits */
implicit def gen[T]: Default[T] = macro Magnolia.gen[T]
}
diff --git a/examples/shared/src/main/scala/show.scala b/examples/shared/src/main/scala/show.scala
index 50b34ee..9f634ba 100644
--- a/examples/shared/src/main/scala/show.scala
+++ b/examples/shared/src/main/scala/show.scala
@@ -60,4 +60,10 @@ object Show extends GenericShow[String] {
implicit val int: Show[String, Int] = new Show[String, Int] {
def show(s: Int): String = s.toString
}
+
+ /** show typeclass for sequences */
+ implicit def seq[A](implicit A: Show[String, A]): Show[String, Seq[A]] =
+ new Show[String, Seq[A]] {
+ def show(as: Seq[A]): String = as.iterator.map(A.show).mkString("[", ",", "]")
+ }
}
diff --git a/tests/src/main/scala/tests.scala b/tests/src/main/scala/tests.scala
index 727fb70..6d883da 100644
--- a/tests/src/main/scala/tests.scala
+++ b/tests/src/main/scala/tests.scala
@@ -6,8 +6,6 @@ import contextual.data.scalac._
import contextual.data.fqt._
import contextual.data.txt._
-import scala.util._
-
sealed trait Tree[+T]
case class Leaf[+L](value: L) extends Tree[L]
case class Branch[+B](left: Tree[B], right: Tree[B]) extends Tree[B]
@@ -37,11 +35,30 @@ case object Blue extends Color
case class `%%`(`/`: Int, `#`: String)
+case class Param(a: String, b: String)
+case class Test(param: Param)
+object Test {
+ def apply(): Test = Test(Param("", ""))
+
+ def apply(a: String)(implicit b: Int): Test = Test(Param(a, b.toString))
+
+ def apply(a: String, b: String): Test = Test(Param(a, b))
+}
+
+case class Account(id: String, emails: String*)
+
+case class Portfolio(companies: Company*)
+
object Tests extends TestApp {
def tests() = for (i <- 1 to 1000) {
import examples._
+ test("construct a Show product instance with alternative apply functions") {
+ import examples._
+ Show.gen[Test].show(Test("a", "b"))
+ }.assert(_ == """Test(param=Param(a=a,b=b))""")
+
test("construct a Show product instance") {
import examples._
Show.gen[Person].show(Person("John Smith", 34))
@@ -161,6 +178,16 @@ object Tests extends TestApp {
| in parameter 'alpha' of product type Beta
|"""))
+ test("not attempt to instantiate Unit when producing error stack") {
+ scalac"""
+ import magnolia.examples._
+ case class Gamma(unit: Unit)
+ Show.gen[Gamma]
+ """
+ }.assert(_ == TypecheckError(txt"""magnolia: could not find typeclass for type Unit
+ | in parameter 'unit' of product type Gamma
+ |"""))
+
test("typenames and labels are not encoded") {
implicitly[Show[String, `%%`]].show(`%%`(1, "two"))
}.assert(_ == "%%(/=1,#=two)")
@@ -177,6 +204,7 @@ object Tests extends TestApp {
Show.gen[Length].show(new Length(100))
}.assert(_ == "100")
+
class ParentClass {
case class InnerClass(name: String)
@@ -186,5 +214,18 @@ object Tests extends TestApp {
}
new ParentClass
+
+ test("show an Account") {
+ Show.gen[Account].show(Account("john_doe", "john.doe@yahoo.com", "john.doe@gmail.com"))
+ }.assert(_ == "Account(id=john_doe,emails=[john.doe@yahoo.com,john.doe@gmail.com])")
+
+ test("construct a default Account") {
+ Default.gen[Account].default
+ }.assert(_ == Account(""))
+
+ test("show a Portfolio of Companies") {
+ Show.gen[Portfolio].show(Portfolio(Company("Alice Inc"), Company("Bob & Co")))
+ }.assert(_ == "Portfolio(companies=[Company(name=Alice Inc),Company(name=Bob & Co)])")
+
}
}