Skip to content

Commit

Permalink
When case class has custom unapply, reference getters instead
Browse files Browse the repository at this point in the history
  • Loading branch information
joroKr21 committed Mar 26, 2022
1 parent afa492d commit 5ca40dd
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 7 deletions.
21 changes: 15 additions & 6 deletions core/src/main/scala/shapeless/generic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -368,10 +368,14 @@ trait CaseClassMacros extends ReprTypes with CaseClassMacrosVersionSpecifics {
* */
def fieldsOf(tpe: Type): List[(TermName, Type)] = {
val clazz = tpe.typeSymbol.asClass
// Case class field names have an extra space at the end.
val nameOf: TermSymbol => TermName =
if (!clazz.isCaseClass) _.name
else field => TermName(field.name.toString.dropRight(1))
if (isCaseObjectLike(clazz) || isAnonOrRefinement(clazz)) Nil
else tpe.decls.sorted.collect {
case sym: TermSymbol if isCaseAccessorLike(sym) =>
(sym.name, sym.typeSignatureIn(tpe).finalResultType)
case field: TermSymbol if isCaseAccessorLike(field) =>
nameOf(field) -> field.typeSignatureIn(tpe).finalResultType
}
}

Expand Down Expand Up @@ -496,11 +500,14 @@ trait CaseClassMacros extends ReprTypes with CaseClassMacrosVersionSpecifics {
}
}

def nameAsString(name: Name): String = name.decodedName.toString.trim
def nameAsString(name: Name): String =
name.decodedName.toString

def nameAsValue(name: Name): Constant = Constant(nameAsString(name))
def nameAsValue(name: Name): Constant =
Constant(nameAsString(name))

def nameOf(tpe: Type) = tpe.typeSymbol.name
def nameOf(tpe: Type): Name =
tpe.typeSymbol.name

def mkHListValue(elems: List[Tree]): Tree = {
val cons = objectRef[::.type]
Expand Down Expand Up @@ -1030,8 +1037,10 @@ trait CaseClassMacros extends ReprTypes with CaseClassMacrosVersionSpecifics {
(const(singleton), const(objectRef[HNil.type]))
// case 3: case class
case tpe if tpe.typeSymbol.asClass.isCaseClass =>
val companion = patchedCompanionSymbolOf(tpe.typeSymbol)
val unapply = companion.typeSignature.member(TermName("unapply"))
val fields = fieldsOf(tpe)
(fromApply(fields), toUnapply(fields))
(fromApply(fields), if (unapply.isSynthetic) toUnapply(fields) else toGetters(fields))
// case 4: exactly one matching public apply/unapply
case HasApplyUnapply(args) =>
(fromApply(args), toUnapply(args))
Expand Down
26 changes: 25 additions & 1 deletion core/src/test/scala_2.13+/shapeless/GenericTests213.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package shapeless

import shapeless.test.illTyped
import org.junit.Assert.assertEquals
import org.junit.Test
import shapeless.test.{illTyped, typed}
import shapeless.testutil.assertTypedEquals

object GenericTests213 {
case class WrongApplySignature private(value: String)
Expand All @@ -9,5 +12,26 @@ object GenericTests213 {
def apply(v: String): Either[String, WrongApplySignature] = Left("No ways")
}

case class CCWithCustomUnapply(x: Int, y: String)
object CCWithCustomUnapply {
def unapply(cc: CCWithCustomUnapply): Option[(Int, String, String)] = None
}
}

class GenericTests213 {
import GenericTests213._

illTyped("Generic[WrongApplySignature]")

@Test
def testCCWithCustomUnapply(): Unit = {
val cc = CCWithCustomUnapply(23, "foo")
val gen = Generic[CCWithCustomUnapply]
val r = gen.to(cc)
val f = gen.from(13 :: "bar" :: HNil)
assertTypedEquals[Int :: String :: HNil](23 :: "foo" :: HNil, r)
typed[CCWithCustomUnapply](f)
assertEquals(13, f.x)
assertEquals("bar", f.y)
}
}

0 comments on commit 5ca40dd

Please # to comment.