Skip to content

Commit

Permalink
Fix issue 545 causing case class default values to fail to resolve in…
Browse files Browse the repository at this point in the history
… Scala 3.x (#547)

Fixes #545
  • Loading branch information
lihaoyi authored Jan 11, 2024
1 parent ac82000 commit 04b34f7
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .mill-version
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
0.11.2
0.11.6

11 changes: 7 additions & 4 deletions upickle/implicits/src-3/upickle/implicits/macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ type IsInt[A <: Int] = A

def getDefaultParamsImpl0[T](using Quotes, Type[T]): Map[String, Expr[AnyRef]] =
import quotes.reflect._
val sym = TypeTree.of[T].symbol
val unwrapped = TypeRepr.of[T] match{case AppliedType(p, v) => p case t => t}
val sym = unwrapped.typeSymbol

if (!sym.isClassDef) Map.empty
else
Expand All @@ -23,14 +24,16 @@ def getDefaultParamsImpl0[T](using Quotes, Type[T]): Map[String, Expr[AnyRef]] =

val body = comp.tree.asInstanceOf[ClassDef].body

val idents: List[Ref] =
val idents: List[Term] =
for case deff @ DefDef(name, _, _, _) <- body
if name.startsWith("$lessinit$greater$default")
yield Ref(deff.symbol)
yield TypeRepr.of[T] match{ // Fix copied from https://github.com/circe/circe/issues/2093
case AppliedType(p, v) => Ref(deff.symbol).appliedToTypes(TypeRepr.of[T].typeArgs)
case t => Ref(deff.symbol)
}

names.zip(idents.map(_.asExpr).map(e => '{$e.asInstanceOf[AnyRef]})).toMap


def extractKey[A](using Quotes)(sym: quotes.reflect.Symbol): Option[String] =
import quotes.reflect._
sym
Expand Down
22 changes: 22 additions & 0 deletions upickle/test/src/upickle/MacroTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ object SpecialChars{
implicit def rw: RW[SpecialChars] = default.macroRW
}

object GenericIssue545{
case class Person(id: Int, name: String = "test")

implicit val personRw: upickle.default.ReadWriter[Person] = upickle.default.macroRW[Person]

case class ApiResult[T](data: Option[T] = None, @upickle.implicits.key("total_count") totalCount: Int)

implicit def apiResultRw[T: upickle.default.ReadWriter]: upickle.default.ReadWriter[ApiResult[T]] = upickle.default.macroRW[ApiResult[T]]
}

object MacroTests extends TestSuite {

// Doesn't work :(
Expand Down Expand Up @@ -564,5 +574,17 @@ object MacroTests extends TestSuite {
rw(SpecialChars.`-1`(), """{"$type": "upickle.SpecialChars.-1"}""")
rw(SpecialChars.`-1`(1), """{"$type": "upickle.SpecialChars.-1", "-1": 1}""")
}

test("genericIssue545"){
// Make sure case class default values are properly picked up for
// generic case classes in Scala 3
import upickle.implicits.key

upickle.default.read[GenericIssue545.Person]("{\"id\":1}") ==>
GenericIssue545.Person(1)

upickle.default.read[GenericIssue545.ApiResult[GenericIssue545.Person]]("{\"total_count\": 10}") ==>
GenericIssue545.ApiResult[GenericIssue545.Person](None, 10)
}
}
}

0 comments on commit 04b34f7

Please sign in to comment.