diff --git a/core/shared/src/main/scala/shapeless/generic.scala b/core/shared/src/main/scala/shapeless/generic.scala index 9f3dcc261..86b3c6b7f 100644 --- a/core/shared/src/main/scala/shapeless/generic.scala +++ b/core/shared/src/main/scala/shapeless/generic.scala @@ -593,18 +593,23 @@ trait CaseClassMacros extends ReprTypes with CaseClassMacrosVersionSpecifics { else mkCoproductTypTree1(ctorsOf1(tpe), param, arg) } + /** Returns the parameter lists of `tpe`, removing any implicit parameters. */ + private def nonImplicitParamLists(tpe: Type): List[List[Symbol]] = + tpe.paramLists.takeWhile(params => params.isEmpty || !params.head.isImplicit) + def isCaseClassLike(sym: ClassSymbol): Boolean = { def isConcrete = !(sym.isAbstract || sym.isTrait || sym == symbolOf[Object]) def isFinalLike = sym.isFinal || sym.knownDirectSubclasses.isEmpty - def ctor = for { - ctor <- accessiblePrimaryCtorOf(sym.typeSignature) - Seq(params) <- Option(ctor.typeSignature.paramLists) - if params.size == fieldsOf(sym.typeSignature).size - } yield ctor - sym.isCaseClass || (isConcrete && isFinalLike && ctor.isDefined) + def constructor = for { + constructor <- accessiblePrimaryCtorOf(sym.typeSignature) + Seq(params) <- Option(nonImplicitParamLists(constructor.typeSignature)) + if params.length == fieldsOf(sym.typeSignature).length + } yield constructor + sym.isCaseClass || (isConcrete && isFinalLike && constructor.isDefined) } - def isCaseObjectLike(sym: ClassSymbol): Boolean = sym.isModuleClass + def isCaseObjectLike(sym: ClassSymbol): Boolean = + sym.isModuleClass def isCaseAccessorLike(sym: TermSymbol, inCaseClass: Boolean): Boolean = { val isGetter = @@ -777,26 +782,17 @@ trait CaseClassMacros extends ReprTypes with CaseClassMacrosVersionSpecifics { def numNonCaseParamLists(tpe: Type): Int = { val companion = patchedCompanionSymbolOf(tpe.typeSymbol).typeSignature val apply = companion.member(TermName("apply")) - if (apply.isMethod && !isNonGeneric(apply) && isAccessible(companion, apply)) { - val paramLists = apply.typeSignatureIn(companion).paramLists - val numParamLists = paramLists.length - if (numParamLists <= 1) 0 - else { - if (paramLists.last.headOption.exists(_.isImplicit)) - numParamLists-2 - else - numParamLists-1 - } - } else 0 + if (!apply.isMethod || isNonGeneric(apply) || !isAccessible(companion, apply)) 0 + else nonImplicitParamLists(apply.typeSignatureIn(companion)).length.max(1) - 1 } object HasApply { def unapply(tpe: Type): Option[List[(TermName, Type)]] = for { companion <- Option(patchedCompanionSymbolOf(tpe.typeSymbol).typeSignature) - apply = companion.member(TermName("apply")) + apply <- Option(companion.member(TermName("apply"))) if apply.isMethod && !isNonGeneric(apply) if isAccessible(companion, apply) - Seq(params) <- Option(apply.typeSignatureIn(companion).paramLists) + Seq(params) <- Option(nonImplicitParamLists(apply.typeSignatureIn(companion))) aligned <- alignFields(tpe, for (param <- params) yield param.name.toTermName -> param.typeSignature) } yield aligned @@ -805,19 +801,19 @@ trait CaseClassMacros extends ReprTypes with CaseClassMacrosVersionSpecifics { object HasUnapply { def unapply(tpe: Type): Option[List[Type]] = for { companion <- Option(patchedCompanionSymbolOf(tpe.typeSymbol).typeSignature) - unapply = companion.member(TermName("unapply")) + unapply <- Option(companion.member(TermName("unapply"))) if unapply.isMethod && !isNonGeneric(unapply) if isAccessible(companion, unapply) - returnTpe <- unapply.asMethod.typeSignatureIn(companion).finalResultType + returnTpe <- unapply.typeSignatureIn(companion).finalResultType .baseType(symbolOf[Option[_]]).typeArgs.headOption } yield if (returnTpe <:< typeOf[Product]) returnTpe.typeArgs else List(returnTpe) } object HasUniqueCtor { def unapply(tpe: Type): Option[List[(TermName, Type)]] = for { - ctor <- accessiblePrimaryCtorOf(tpe) - if !isNonGeneric(ctor) - Seq(params) <- Option(ctor.typeSignatureIn(tpe).paramLists) + constructor <- accessiblePrimaryCtorOf(tpe) + if !isNonGeneric(constructor) + Seq(params) <- Option(nonImplicitParamLists(constructor.typeSignatureIn(tpe))) aligned <- alignFields(tpe, for (param <- params) yield param.name.toTermName -> param.typeSignature) } yield aligned diff --git a/core/shared/src/test/scala/shapeless/generic.scala b/core/shared/src/test/scala/shapeless/generic.scala index 7599bdfe7..8543a6cb9 100644 --- a/core/shared/src/test/scala/shapeless/generic.scala +++ b/core/shared/src/test/scala/shapeless/generic.scala @@ -143,7 +143,12 @@ package GenericTestsAux { } case class CCOrdered[A: Ordering](value: A) - class CCLikeOrdered[A: Ordering](val value: A) + class CCLikeOrdered[A: Ordering](val value: A) { + override def equals(that: Any): Boolean = that match { + case that: CCLikeOrdered[_] => this.value == that.value + case _ => false + } + } case class CCDegen(i: Int)() class CCLikeDegen(val i: Int)() @@ -841,13 +846,15 @@ class GenericTests { @Test def testGenericImplicitParams: Unit = { type Repr = Int :: HNil - val gen = Generic[CCOrdered[Int]] - val cc = CCOrdered(42) + val gen1 = Generic[CCOrdered[Int]] + val gen2 = Generic[CCLikeOrdered[Int]] + val cc1 = CCOrdered(42) + val cc2 = new CCLikeOrdered(42) val rep = 42 :: HNil - - assertTypedEquals[CCOrdered[Int]](gen.from(rep), cc) - assertTypedEquals[Repr](gen.to(cc), rep) - illTyped("Generic[CCLikeOrdered[Int]]") + assertTypedEquals[CCOrdered[Int]](gen1.from(rep), cc1) + assertTypedEquals[CCLikeOrdered[Int]](gen2.from(rep), cc2) + assertTypedEquals[Repr](gen1.to(cc1), rep) + assertTypedEquals[Repr](gen2.to(cc2), rep) } @Test