Skip to content

Commit

Permalink
Merge pull request #525 from WojciechMazur/fix/reduce-dependency-on-s…
Browse files Browse the repository at this point in the history
…ymbol-tree

Reduce number of `Symbol.tree` dependencies  in macros.
  • Loading branch information
KacperFKorban authored Apr 19, 2024
2 parents 1ef91ff + a8d17a9 commit 25ddaf9
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 36 deletions.
1 change: 0 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ lazy val test = (projectMatrix in file("test"))
.settings(commonSettings)
.settings(
name := "magnolia-test",
scalacOptions += "-Yretain-trees",
projectDependencies ++= Seq(
"org.scalameta" %%% "munit" % "1.0.0-M12"
),
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/magnolia1/interface.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ object CaseClass:
*/
def deref(param: Type): PType

/** Requires compilation with `-Yretain-trees` on.
/** Recommended compilation with `-Yretain-trees` on.
* @return
* default argument value, if any
*/
Expand Down
77 changes: 44 additions & 33 deletions core/src/main/scala/magnolia1/macro.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,24 @@ object Macro:
.zipWithIndex
.map { case (field, i) =>
exprOfOption {
val defaultMethodName = s"$$lessinit$$greater$$default$$${i + 1}"
Expr(field.name) -> tpe.companionClass
.declaredMethod(s"$$lessinit$$greater$$default$$${i + 1}")
.declaredMethod(defaultMethodName)
.headOption
.flatMap(_.tree.asInstanceOf[DefDef].rhs)
.map { defaultMethod =>
val callDefault = {
val base = Ident(tpe.companionModule.termRef).select(defaultMethod)
val tParams = defaultMethod.paramSymss.headOption.filter(_.forall(_.isType))
tParams match
case Some(tParams) => TypeApply(base, tParams.map(TypeTree.ref))
case _ => base
}

defaultMethod.tree match {
case tree: DefDef => tree.rhs.getOrElse(callDefault)
case _ => callDefault
}
}
.map(_.asExprOf[Any])
}
}
Expand All @@ -87,14 +101,10 @@ object Macro:
case _ => Nil

Expr.ofList {
TypeRepr
.of[T]
.typeSymbol
.caseFields
val typeRepr = TypeRepr.of[T]
typeRepr.typeSymbol.caseFields
.map { field =>
val tpeRepr = field.tree match
case v: ValDef => v.tpt.tpe
case d: DefDef => d.returnTpt.tpe
val tpeRepr = typeRepr.memberType(field)

Expr(field.name) -> getAnnotations(tpeRepr)
.filter { a =>
Expand All @@ -110,25 +120,20 @@ object Macro:
def repeated[T: Type](using Quotes): Expr[List[(String, Boolean)]] =
import quotes.reflect.*

def isRepeated[T](tpeRepr: TypeRepr): Boolean = tpeRepr match
case a: AnnotatedType =>
a.annotation.tpe match
case tr: TypeRef => tr.name == "Repeated"
case _ => false
case _ => false

val tpe = TypeRepr.of[T]
val symbol: Option[Symbol] =
if tpe.typeSymbol.isNoSymbol then None else Some(tpe.typeSymbol)
val constr: Option[DefDef] =
symbol.map(_.primaryConstructor.tree.asInstanceOf[DefDef])

val areRepeated = constr.toList
.flatMap(_.paramss)
.flatMap(_.params.flatMap {
case ValDef(name, tpeTree, _) => Some(name -> isRepeated(tpeTree.tpe))
case _ => None
})
val areRepeated =
if tpe.typeSymbol.isNoSymbol then Nil
else {
val symbol = tpe.typeSymbol
val ctor = symbol.primaryConstructor
for param <- ctor.paramSymss.flatten
yield
val isRepeated = tpe.memberType(param) match {
case AnnotatedType(_, annot) => annot.tpe.typeSymbol == defn.RepeatedAnnot
case _ => false
}
param.name -> isRepeated
}

Expr(areRepeated)

Expand Down Expand Up @@ -209,7 +214,13 @@ object Macro:
.filter(filterAnnotation)
.map(_.asExpr.asInstanceOf[Expr[Any]])
case _ =>
List.empty
// Best effort in case whe -Yretain-trees is not used
// Does not support class parent annotations (in the extends clouse)
tpe.baseClasses
.map(tpe.baseType(_))
.flatMap(getAnnotations(_))
.filter(filterAnnotation)
.map(_.asExpr)
}
}
}
Expand Down Expand Up @@ -247,11 +258,11 @@ object Macro:
private def fromDeclarations(
from: Symbol
): List[(String, List[Expr[Any]])] =
from.declarations.collect {
case field: Symbol if field.tree.isInstanceOf[ValDef] =>
field.name -> field.annotations
.filter(filterAnnotation)
.map(_.asExpr.asInstanceOf[Expr[Any]])
from.fieldMembers.collect { case field: Symbol =>
val annotations = field.annotations ::: field.allOverriddenSymbols.flatMap(_.annotations).toList
field.name -> annotations
.filter(filterAnnotation)
.map(_.asExpr.asInstanceOf[Expr[Any]])
}

private def groupByParamName(anns: List[(String, List[Expr[Any]])]) =
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ be automatically available for consideration during contextual search.
If you don't want to make the automatic derivation available in the given scope, consider using the `Derivation` trait which provides semi-auto derivation with `derived` method, but also brings some additional limitations.
## Limitations

Accessing default values for case class parameters requires compilation with `-Yretain-trees` on.
For accessing default values for case class parameters we recommend compilation with `-Yretain-trees` on.

For a recursive structures it is required to assign the derived value to an implicit variable e.g.
```Scala
Expand Down

0 comments on commit 25ddaf9

Please sign in to comment.