Skip to content

Commit

Permalink
Merge branch 'scala3' into update/scala3-library-3.3.3
Browse files Browse the repository at this point in the history
  • Loading branch information
kciesielski committed Apr 25, 2024
2 parents 7df0172 + 267acc4 commit ecaf0b1
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 50 deletions.
3 changes: 3 additions & 0 deletions .scala-steward.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ updates.ignore = [
{groupId = "org.scala-lang", artifactId = "scala-compiler", version = "2.13."},
{groupId = "org.scala-lang", artifactId = "scala-compiler", version = "3."}
]
updates.pin = [
{groupId = "org.scala-lang", artifactId = "scala3-library", version = "3.3."}
]
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
version = 3.8.0
version = 3.8.1
runner.dialect = scala3
maxColumn = 140
3 changes: 1 addition & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,8 @@ lazy val test = (projectMatrix in file("test"))
.settings(commonSettings)
.settings(
name := "magnolia-test",
scalacOptions += "-Yretain-trees",
projectDependencies ++= Seq(
"org.scalameta" %%% "munit" % "1.0.0-M6"
"org.scalameta" %%% "munit" % "1.0.0-M12"
),
testFrameworks += new TestFramework("munit.Framework"),
publishArtifact := false
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
84 changes: 48 additions & 36 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 All @@ -229,11 +240,11 @@ object Macro:
.filterNot(isObjectOrScala)
.collect {
case s if s != tpe.typeSymbol =>
(fromConstructor(s) ++ fromDeclarations(s)).filter { case (_, anns) =>
(fromConstructor(s)).filter { case (_, anns) =>
anns.nonEmpty
}
}
.flatten
.flatten ++ fromDeclarations(tpe.typeSymbol, inherited = true)
}
}

Expand All @@ -245,13 +256,14 @@ object Macro:
}

private def fromDeclarations(
from: Symbol
from: Symbol,
inherited: Boolean = false
): 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 = if (!inherited) field.annotations else 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 examples/src/main/scala/magnolia1/examples/show.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ trait GenericShow[Out] extends AutoDerivation[[X] =>> Show[Out, X]] {
if (param.annotations.isEmpty && param.inheritedAnnotations.isEmpty)
""
else {
(param.annotations ++ param.inheritedAnnotations).distinct
(param.annotations.map(_.toString) ++ param.inheritedAnnotations.map(a => s"[i]$a")).distinct
.mkString("{", ",", "}")
}

Expand Down
6 changes: 3 additions & 3 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.9.2")
addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.10.0")

val sbtSoftwareMillVersion = "2.0.20"
addSbtPlugin(
Expand All @@ -8,6 +8,6 @@ addSbtPlugin(
"com.softwaremill.sbt-softwaremill" % "sbt-softwaremill-publish" % sbtSoftwareMillVersion
)

addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.15.0")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.17")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.1")
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.3")
4 changes: 2 additions & 2 deletions 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 All @@ -88,7 +88,7 @@ given instance: SemiPrint[Recursive] = SemiPrint.derived
For Scala 3:

```scala
val magnolia = "com.softwaremill.magnolia1_3" %% "magnolia" % "1.3.4"
val magnolia = "com.softwaremill.magnolia1_3" %% "magnolia" % "1.3.5"
```

For Scala 2, see the [scala2 branch](https://github.com/softwaremill/magnolia/tree/scala2).
Expand Down
9 changes: 5 additions & 4 deletions test/src/test/scala/magnolia1/tests/AnnotationsTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class AnnotationsTests extends munit.FunSuite:
val res = Show.derived[Pet].show(Dog("Alex", 10, likesMeat = true))
assertEquals(
res,
"{MyTypeAnnotation(2),MyTypeAnnotation(1)}Dog{MyTypeAnnotation(2),MyTypeAnnotation(1)}(name{MyAnnotation(1)}=Alex,age{MyAnnotation(2)}=10,likesMeat{MyAnnotation(3)}=true)"
"{MyTypeAnnotation(2),MyTypeAnnotation(1)}Dog{MyTypeAnnotation(2),MyTypeAnnotation(1)}(name{[i]MyAnnotation(1)}=Alex,age{[i]MyAnnotation(2)}=10,likesMeat{MyAnnotation(3)}=true)"
)
}

Expand All @@ -35,13 +35,13 @@ class AnnotationsTests extends munit.FunSuite:
.show(Hamster("Alex", 10, likesNuts = true, likesVeggies = true))
assertEquals(
res,
"{MyTypeAnnotation(1)}Hamster{MyTypeAnnotation(1)}(name{MyAnnotation(1)}=Alex,age{MyAnnotation(2)}=10,likesNuts{MyAnnotation(3)}=true,likesVeggies{MyAnnotation(4)}=true)"
"{MyTypeAnnotation(1)}Hamster{MyTypeAnnotation(1)}(name{[i]MyAnnotation(1)}=Alex,age{MyAnnotation(6),[i]MyAnnotation(2)}=10,likesNuts{[i]MyAnnotation(3)}=true,likesVeggies{MyAnnotation(4)}=true)"
)
}

test("inherit annotations from base class constructor parameters") {
val res = Show.derived[Foo].show(Foo("foo"))
assertEquals(res, "Foo(foo{MyAnnotation(2),MyAnnotation(1)}=foo)")
assertEquals(res, "Foo(foo{MyAnnotation(2),[i]MyAnnotation(1)}=foo)")
}

test(
Expand All @@ -50,7 +50,7 @@ class AnnotationsTests extends munit.FunSuite:
val res = Show.derived[Bar].show(Bar("foo", "bar"))
assertEquals(
res,
"Bar(foo{MyAnnotation(2),MyAnnotation(1)}=foo,bar{MyAnnotation(2),MyAnnotation(1)}=bar)"
"Bar(foo{MyAnnotation(2),[i]MyAnnotation(1)}=foo,bar{MyAnnotation(2),[i]MyAnnotation(1)}=bar)"
)
}

Expand Down Expand Up @@ -156,6 +156,7 @@ object AnnotationsTests:

case class Hamster(
name: String,
@MyAnnotation(6)
age: Int,
likesNuts: Boolean,
@MyAnnotation(4) likesVeggies: Boolean
Expand Down

0 comments on commit ecaf0b1

Please sign in to comment.