circe / circe-derivation

Fast type class instance derivation for Circe
132 stars 30 forks source link

Code spliced for handling default parameters mentions companion object, potentially doesn't have to? #113

Open neko-kai opened 5 years ago

neko-kai commented 5 years ago

Derivation in this code:

final case class RandomPayload(field1: String, randomArray: Set[Int] = List.fill(10)(Random.nextInt(10)).toSet)

object RandomPayload extends WithCirce[RandomPayload]

Will produce the following error:

super constructor cannot be passed a self reference unless parameter is declared by-name
[error]   object RandomPayload extends WithCirce[RandomPayload]
[error]                                ^

Where WithCirce is defined as:

import io.circe.{Decoder, Encoder, derivation}

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

/**
 * Provides circe codecs for case classes and sealed traits
 * {{{
 *   final case class Abc(a: String, b: String, c: String)
 *
 *   object Abc extends WithCirce[Abc]
 * }}}
 */
abstract class WithCirce[A](implicit encoder: DerivationDerivedEncoder[A], decoder: DerivationDerivedDecoder[A]) {
  implicit val enc: Encoder.AsObject[A] = encoder.value
  implicit val dec: Decoder[A] = decoder.value
}

final class MaterializeDerivationMacros(override val c: blackbox.Context) extends derivation.DerivationMacros(c) {
  import c.universe._

  def materializeEncoderImpl[A: c.WeakTypeTag]: c.Expr[DerivationDerivedEncoder[A]] =
    c.Expr[DerivationDerivedEncoder[A]] {
      q"{ ${symbolOf[DerivationDerivedEncoder.type].asClass.module}.apply(${materializeEncoder[A]}) }"
    }

  def materializeDecoderImpl[A: c.WeakTypeTag]: c.Expr[DerivationDerivedDecoder[A]] =
    c.Expr[DerivationDerivedDecoder[A]] {
      q"{ ${symbolOf[DerivationDerivedDecoder.type].asClass.module}.apply(${materializeDecoder[A]}) }"
    }
}

final case class DerivationDerivedEncoder[A](value: Encoder.AsObject[A])
object DerivationDerivedEncoder {
  implicit def materialize[A]: DerivationDerivedEncoder[A] = macro MaterializeDerivationMacros.materializeEncoderImpl[A]
}

final case class DerivationDerivedDecoder[A](value: Decoder[A])
object DerivationDerivedDecoder {
  implicit def materialize[A]: DerivationDerivedDecoder[A] = macro MaterializeDerivationMacros.materializeDecoderImpl[A]
}

If the default parameter is removed, the error disappears:

final case class RandomPayload(field1: String, randomArray: Set[Int])
object RandomPayload extends WithCirce[RandomPayload]

This didn't happen in older versions of circe-derivation and indicates that the code for default value management is splicing additional references to the companion object – which I'm not sure are necessary for the use case, although I may be wrong.

/cc @aparo

travisbrown commented 5 years ago

Thanks for catching this. I'll try to take a look this weekend.

voidconductor commented 5 years ago

Derivation also fails if there's default parameters in companion object's apply method, it could be related

case class Test(field1: Option[String], field2: Int, field3: Boolean)

object Test {
  def apply(value: List[String], bool: Boolean = false): Test = 
    Test(value.headOption, value.length, bool)
}

io.circe.derivation.deriveDecoder[Test]
cmd7.sc:1: type mismatch;
 found   : Boolean
 required: Int
val res7 = io.circe.derivation.deriveDecoder[Test]
                                            ^
Compilation Failed