aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorKevin Wright <kevin.wright@bradyplc.com>2018-02-01 08:30:38 +0000
committerKevin Wright <kevin.wright@bradyplc.com>2018-02-01 08:30:38 +0000
commita346b10556f8958834c14588e3443d3922d66f89 (patch)
tree261d69b43c4e2ee1fae83b9b22c4001e1fb3145f /core
parent4d1bf3ad68b3def3bbdc7ac8c45cb1a72c2c4e09 (diff)
downloadmagnolia-a346b10556f8958834c14588e3443d3922d66f89.tar.gz
magnolia-a346b10556f8958834c14588e3443d3922d66f89.tar.bz2
magnolia-a346b10556f8958834c14588e3443d3922d66f89.zip
Added annotation capture to params
Added test for annotation capture Updated to lamdafied syntax for SAM type construction Minor changes to permit compilation under JDK 9 Added Kevin Wright as a contributor
Diffstat (limited to 'core')
-rw-r--r--core/shared/src/main/scala/interface.scala12
-rw-r--r--core/shared/src/main/scala/magnolia.scala56
2 files changed, 44 insertions, 24 deletions
diff --git a/core/shared/src/main/scala/interface.scala b/core/shared/src/main/scala/interface.scala
index 846eee9..427c84c 100644
--- a/core/shared/src/main/scala/interface.scala
+++ b/core/shared/src/main/scala/interface.scala
@@ -25,6 +25,8 @@ trait Subtype[Typeclass[_], Type] {
/** partial function defined the subset of values of `Type` which have the type of this subtype */
def cast: PartialFunction[Type, SType]
+
+ override def toString: String = s"Subtype(${typeName.full})"
}
/** represents a parameter of a case class
@@ -89,6 +91,10 @@ trait Param[Typeclass[_], Type] {
* @param param the instance of the case class to be dereferenced
* @return the parameter value */
def dereference(param: Type): PType
+
+ def annotations: Seq[Any]
+
+ override def toString: String = s"Param($label)"
}
/** represents a case class or case object and the context required to construct a new typeclass
@@ -110,6 +116,7 @@ abstract class CaseClass[Typeclass[_], Type] private[magnolia] (
parametersArray: Array[Param[Typeclass, Type]]
) {
+ override def toString: String = s"CaseClass(${typeName.full}, ${parameters.mkString(",")})"
/** constructs a new instance of the case class type
*
* This method will be implemented by the Magnolia macro to make it possible to construct
@@ -128,7 +135,7 @@ abstract class CaseClass[Typeclass[_], Type] private[magnolia] (
/** constructs a new instance of the case class type
*
- * Like [[construct]] this method is implemented by Magnolia and let's you construct case class
+ * Like [[construct]] this method is implemented by Magnolia and lets you construct case class
* instances generically in user code, without knowing their type concretely.
*
* `rawConstruct`, however, is more low-level in that it expects you to provide a [[Seq]]
@@ -152,7 +159,6 @@ abstract class CaseClass[Typeclass[_], Type] private[magnolia] (
*
* Instances of `SealedTrait` provide access to all of the component subtypes of the sealed trait
* which form a coproduct, and to the fully-qualified name of the sealed trait.
- *
* @param typeName the name of the sealed trait
* @param subtypesArray an array of [[Subtype]] instances for each subtype in the sealed trait
* @tparam Typeclass type constructor for the typeclass being derived
@@ -160,6 +166,8 @@ abstract class CaseClass[Typeclass[_], Type] private[magnolia] (
final class SealedTrait[Typeclass[_], Type](val typeName: TypeName,
subtypesArray: Array[Subtype[Typeclass, Type]]) {
+ override def toString: String = s"SealedTrait($typeName, Array[${subtypes.mkString(",")}])"
+
/** a sequence of all the subtypes of this sealed trait */
def subtypes: Seq[Subtype[Typeclass, Type]] = subtypesArray
diff --git a/core/shared/src/main/scala/magnolia.scala b/core/shared/src/main/scala/magnolia.scala
index a1d4e8a..2e13061 100644
--- a/core/shared/src/main/scala/magnolia.scala
+++ b/core/shared/src/main/scala/magnolia.scala
@@ -73,6 +73,8 @@ object Magnolia {
val magnoliaPkg = c.mirror.staticPackage("magnolia")
val scalaPkg = c.mirror.staticPackage("scala")
+ val sciPkg = c.mirror.staticPackage("scala.collection.immutable")
+
val repeatedParamClass = definitions.RepeatedParamClass
val scalaSeqType = typeOf[Seq[_]].typeConstructor
@@ -190,6 +192,15 @@ object Magnolia {
"""
Some(impl)
} else if (isCaseClass || isValueClass) {
+
+ val companionRef = GlobalUtil.patchedCompanionRef(c)(genericType.dealias)
+
+ val headParamList = {
+ val primaryConstructor = classType map (_.primaryConstructor)
+ val optList: Option[List[c.universe.Symbol]] = primaryConstructor flatMap (_.typeSignature.paramLists.headOption)
+ optList.map(_.map(_.asTerm))
+ }
+
val caseClassParameters = genericType.decls.collect {
case m: MethodSymbol if m.isCaseAccessor || (isValueClass && m.isParamAccessor) =>
m.asMethod
@@ -199,14 +210,11 @@ object Magnolia {
repeated: Boolean,
typeclass: Tree,
paramType: Type,
- ref: TermName)
+ ref: TermName
+ )
val caseParamsReversed = caseClassParameters.foldLeft[List[CaseParam]](Nil) {
(acc, param) =>
- for (ann <- param.annotations) {
- c.info(c.enclosingPosition, ann.tree.toString, force = false)
- }
-
val paramName = param.name.decodedName.toString
val paramTypeSubstituted = param.typeSignatureIn(genericType).resultType
@@ -242,20 +250,16 @@ object Magnolia {
val preAssignments = caseParams.map(_.typeclass)
- val defaults = if (!isValueClass) {
- val companionRef = GlobalUtil.patchedCompanionRef(c)(genericType.dealias)
- val companionSym = companionRef.symbol.asModule.info
-
- // If a companion object is defined with alternative apply methods
- // it is needed get all the alternatives
- val constructorMethods =
- companionSym.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
+ val defaults = headParamList.filterNot(_ => isValueClass) map { plist =>
+ // note: This causes the namer/typer to generate the synthetic default methods by forcing
+ // the typeSignature of the "default" factory method to be visited.
+ // It feels like it shouldn't be needed, but we'll get errors otherwise (as discovered after 6 hours debugging)
+ val companionSym = companionRef.symbol.asModule.info
+ val primaryFactoryMethod = companionSym.decl(TermName("apply")).alternatives.lastOption
+ primaryFactoryMethod.foreach(_.asMethod.typeSignature)
+ val indexedConstructorParams = plist.zipWithIndex
indexedConstructorParams.map {
case (p, idx) =>
if (p.isParamWithDefault) {
@@ -263,13 +267,17 @@ object Magnolia {
q"$scalaPkg.Some($companionRef.$method)"
} else q"$scalaPkg.None"
}
- } else List(q"$scalaPkg.None")
+ } getOrElse List(q"$scalaPkg.None")
+
+ val annotations: List[List[Tree]] = headParamList.toList.flatten map { param =>
+ param.annotations map { _.tree }
+ }
- val assignments = caseParams.zip(defaults).zipWithIndex.map {
- case ((CaseParam(param, repeated, typeclass, paramType, ref), defaultVal), idx) =>
+ val assignments = caseParams.zip(defaults).zip(annotations).zipWithIndex.map {
+ case (((CaseParam(param, repeated, typeclass, paramType, ref), defaultVal), annList), idx) =>
q"""$paramsVal($idx) = $magnoliaPkg.Magnolia.param[$typeConstructor, $genericType,
$paramType](
- ${param.name.decodedName.toString}, $repeated, $ref, $defaultVal, _.${param.name}
+ ${param.name.decodedName.toString}, $repeated, $ref, $defaultVal, _.${param.name}, $sciPkg.List(..$annList)
)"""
}
@@ -402,6 +410,7 @@ object Magnolia {
def cast: PartialFunction[T, SType] = this
def isDefinedAt(t: T) = isType(t)
def apply(t: T): SType = asType(t)
+ override def toString: String = s"Subtype(${typeName.full})"
}
/** constructs a new [[Param]] instance
@@ -412,13 +421,16 @@ object Magnolia {
isRepeated: Boolean,
typeclassParam: => Tc[P],
defaultVal: => Option[P],
- deref: T => P): Param[Tc, T] = new Param[Tc, T] {
+ deref: T => P,
+ annotationsParam: List[Any]
+ ): Param[Tc, T] = 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)
+ def annotations: Seq[Any] = annotationsParam
}
/** constructs a new [[CaseClass]] instance